multithreading - promises - promise async example
Módulos node.js: Async vs Fibers.promise vs Q_oper8 (4)
Estoy usando la funcionalidad diferida de jQuery en el cliente y jQuery diferida para nodejs en el servidor en lugar de devoluciones de llamada anidadas. Ha reducido enormemente el código y ha hecho las cosas tan legibles.
http://techishard.wordpress.com/2012/05/29/making-mongoose-keep-its-promises-on-the-server/
Solo me pregunto si alguien podría darme una comparación de las compensaciones entre estos módulos para manejar eventos asíncronos. Específicamente, me interesa conocer las razones para usar Async en lugar de Fibers.promise, que estoy usando bastante al menos en mi código de prueba en este momento. En particular, una de las principales ventajas que veo en Fibres.promise es que puedo mantener el frente de la cadena de la pila bifurcando, haciendo posible el uso de try { } catch { } finally
, y también permitiéndome asegurar que después de que se haya realizado una solicitud Manejó que la respuesta se termine.
¿Alguien está usando Q_oper8? Encontré esto en otra página y me preguntaba si ya está muerto o si es algo que debería revisar.
La respuesta corta:
- Async es una solución javascript pura / clásica para administrar la asincronía de un solo hilo.
- Fibers es una extensión de node.js para crear coroutines. Incluye una biblioteca de futuros para gestionar la asincronía de un solo hilo.
- Hay muchas otras bibliotecas de futuros (enumeradas a continuación) que no requieren una extensión de javascript.
- Q_oper8 es un módulo node.js para administrar la concurrencia de múltiples procesos
Tenga en cuenta que ninguno de estos ofrece "subprocesos" y, por lo tanto, no se puede decir que ninguno de ellos realice subprocesos múltiples (aunque también hay una extensión node.js para eso: threads_a_gogo ).
Asíncrono vs Fibra / futuros
Asíncrono
Async y Fibres / futuros son diferentes maneras de resolver el mismo problema: administrar las dependencias de forma asíncrona. Async parece tener muchas más "campanas y silbidos" que muchas otras bibliotecas que intentan resolver este problema, lo que en mi opinión lo empeora (mucha más sobrecarga cognitiva, es decir, más basura que aprender).
En javascript la asincronía básica se ve así:
asyncCall(someParam, function(result) {
useThe(result);
});
Si tiene una situación que requiere algo más que una simple asincronía básica, como cuando necesita los resultados de dos llamadas asíncronas, puede hacer algo como esto:
asyncCall1(someParam, function(result0) {
asyncCall2(someParam, function(result1) {
use(result0, result1);
}
});
Ya empieza a parecerse al infierno de devolución de llamada. También es ineficiente porque la segunda llamada está a la espera de que se complete la primera llamada aunque no dependa de ella, sin mencionar que el código ni siquiera hace ningún tipo de manejo razonable de errores. Async proporciona una solución para escribirlo un poco más eficientemente:
async.parallel([
function(callback) {
asyncCall1(someParam, function(result0) {
callback(null,result0);
},
function(callback) {
asyncCall1(someParam, function(result1) {
callback(null,result1);
},
}
],
function(err, results) {
use(results[0], results[1]);
});
Así que para mí, eso es bastante peor que el infierno de devolución de llamada, pero supongo que para cada uno lo suyo. A pesar de ser feo, permite que ambas llamadas se realicen simultáneamente (siempre que realicen llamadas de IO sin bloqueo o algo así). Async tiene muchas más opciones para administrar código asíncrono, por lo que si está interesado, consulte Async .
Introduzca fibra / futuros
El módulo de Fibras incluye una biblioteca de futuros que las utiliza para reinyectar eventos asíncronos en la continuación actual (future.wait ()).
Fibers es diferente de la mayoría de las otras bibliotecas de futuros porque permite que la continuación actual espere un evento asíncrono, lo que significa que no requiere el uso de devoluciones de llamada para que pueda obtener un valor de una solicitud asíncrona, lo que permite que el código asíncrono se convierta como sincrónico Lea sobre coroutines para más sobre eso.
Node.js tiene funciones de io como readFileSync, que te permite esperar en la función en línea mientras recibe el archivo por ti. Esto no es algo que normalmente se hace en javascript, y no es algo que se pueda escribir en javascript puro, requiere una extensión como Fibres.
Volviendo al mismo ejemplo asíncrono anterior, esto es lo que se vería con las fibras / futuros:
var future0 = asyncCall1(someParam);
var future1 = asyncCall2(someParam);
use(future0.wait(), future1.wait());
Esto es drásticamente más simple y tan eficiente como el desastre de Async. Evita el infierno de devolución de llamada de una manera elegante y eficiente. Sin embargo, hay desventajas (menores). David Ellis exageró muchas de las desventajas, así que repetiré la única válida aquí:
Incompatibilidad del navegador
En virtud de que Fibres es una extensión de node.js, no será compatible con los navegadores. Esto hará que sea imposible compartir el código que utiliza fibras tanto con un servidor node.js como con el navegador. Sin embargo, existe un fuerte argumento de que la mayoría del código asíncrono que desea en el servidor (sistema de archivos, base de datos, llamadas de red) no es el mismo código que desea en un navegador (llamadas ajax). Tal vez los tiempos de espera chocan, pero eso parece.
Más allá de eso, el proyecto streamline.js tiene la capacidad de cerrar esta brecha. Parece que tiene un proceso de compilación que puede transformar el código de streamline.js usando sincronización y futuros en javascript puro usando el estilo de devolución de llamada, similar a la ahora no soportada JavaScript narrativa . Streamline.js puede usar un par de mecanismos diferentes detrás de las escenas, uno es node.js Fibers, otro son los generadores ECMAScript 6 y el último es la traducción al javascript de estilo de devolución de llamada que ya mencioné.
Depuración más difícil.
Esta parece ser una queja válida, aunque pequeña. Incluso si solo está planeando usar fibras / futuros, y no usar coroutines para otra cosa, es posible que haya cambios de contexto confusos debido a puntos de salida (y entrada) inesperados de la función.
Introduce la preferencia en javascript
Este es probablemente el problema más importante con las fibras, ya que tiene la posibilidad (aunque poco probable) de introducir errores difíciles de entender. Básicamente, debido a que un yield
Fibra puede causar una salida temporal de un conjunto de código a otra función no determinada, es posible que se pueda leer o introducir algún estado no válido. Ver este artículo para más información. Personalmente, creo que la increíble limpieza de las fibras / futuros y estructuras similares vale la pena de los raros insectos insidiosos. Muchos más errores son causados por código de concurrencia horrible.
Quejas inválidas
- No en Windows: esto ya no es cierto
- Desconocimiento con coroutines: A. Desconocimiento nunca es una razón para evitar algo. Si es bueno, es bueno, sin importar cuán familiarizado esté con él. B. Si bien las coroutinas y los rendimientos pueden ser desconocidos, los futuros son un concepto fácil de entender.
Otras bibliotecas de futuros
Hay muchas bibliotecas que implementan futuros, donde el concepto puede denominarse "futuros", "objetos diferidos" o "promesas". Esto incluye bibliotecas como async-future , streamline.js , Q , when.js , promiscuous , jQuery''s deferred , futuros de coolaj86, promesas de kriszyp y Javascript narrativo .
La mayoría de estos utilizan devoluciones de llamada para resolver los futuros, que solucionan muchos de los problemas que Fibers presenta. Sin embargo, no son tan limpios como las fibras / futuros, aunque son mucho más limpios que Async. Aquí está el mismo ejemplo otra vez usando mi propio async-future :
var future0 = asyncCall1(someParam);
var future1 = asyncCall2(someParam);
Future.all([future0, future1]).then(function(results) {
use(results[0], results[1])
}).done()
Q_oper8
Q_oper8 es realmente una bestia diferente. Ejecuta trabajos en una cola usando un grupo de procesos. Dado que javascript es de un solo hilo *, y javascript no tiene disponible un subproceso nativo, los procesos son la forma habitual de aprovechar más de un procesador en node.js. Q_oper8 está pensado como una alternativa a la gestión de procesos mediante el módulo child_process de node.js.
Nunca he oído hablar de Q_oper8, así que no puedo comentarlo, pero lo veré desde la otra dirección. Escuché sobre Async First y Fiber (y sus bibliotecas auxiliares) en segundo lugar, y no me gusta este último, en realidad.
Las desventajas de la fibra
Desconocimiento para otros desarrolladores de Javascript
Fiber introduce el concepto de co-rutinas a Javascript a través de un método nativo compilado de Fiber
que asume la interpretación del código de Javascript que se le pasa, interceptando llamadas para yield
a la co-rutina en espera.
Es posible que esto no sea importante para usted, pero si necesita trabajar en un equipo, deberá enseñar el concepto a sus miembros (o esperar que tengan experiencia con el concepto en otros idiomas, como Go).
Sin soporte de Windows
Por lo tanto, para utilizar Fiber o cualquiera de las bibliotecas escritas sobre él, primero deberá compilarlo de forma nativa para su plataforma. No uso Windows, pero tenga en cuenta que Fiber no es compatible con Windows, por lo que restringe la utilidad de su propia biblioteca de forma remota. Lo que significa que no encontrará bibliotecas Node.js de propósito general escritas en Fiber (y probablemente no las tendría, de todos modos, ya que agrega un paso de compilación costoso que de otra manera evitaría con async).
Navegador incompatible
Esto significa que cualquier código que escriba usando Fiber no podrá ejecutarse en el navegador, porque no puede mezclar código nativo con el navegador (ni yo, como usuario del navegador, querría que lo haga), incluso si todo lo que escribe es "Javascript "(es Javascript sintácticamente, pero semánticamente no).
Depuración más difícil
Si bien el "infierno de devolución de llamada" puede ser menos agradable a la vista, el estilo de paso de continuación tiene algo muy bueno en sus co-rutinas: usted sabe exactamente dónde ha ocurrido un problema desde la pila de llamadas y puede rastrear hacia atrás. Las Co-Rutinas ingresan a la función en más de un punto del programa y pueden salir de tres tipos de llamadas : return
, throw
y yield()
, donde este último también es un punto de retorno.
Con las co-rutinas, tiene ejecución cruzada entre dos o más funciones que se ejecutan "simultáneamente", y puede tener más de un conjunto de co-rutinas que se ejecutan al mismo tiempo en el bucle de eventos. Con las devoluciones de llamadas tradicionales, se garantiza que el alcance externo de la función es estático durante la ejecución de dicha función, por lo que solo necesita verificar esas variables externas una vez que sean necesarias. Las co-rutinas necesitan que estas comprobaciones se ejecuten después de cada yield()
(ya que su uso con la co-rutina original se traduciría en una cadena de devolución de llamada en Javascript real).
Básicamente, creo que es más difícil trabajar con el concepto de co-rutina porque tiene que existir dentro del bucle de eventos de Javascript, en lugar de ser un método para implementar uno.
¿Qué hace a Async "mejor"?
Peor es mejor
Es una especie de idea de "peor es mejor", en realidad. En lugar de extender el lenguaje Javascript para intentar deshacerse de sus verrugas (y crear otras nuevas, en mi opinión), Async es una solución de Javascript puro para cubrirlas, como el maquillaje.
Control de flujo explícito
Las funciones Async describen diferentes tipos de flujo lógico que deben cruzar la barrera del bucle de eventos, y la biblioteca cubre los detalles de implementación del código de devolución de llamada necesario para implementar esa lógica, y usted simplemente proporciona las funciones que deben ejecutarse aproximadamente en el orden lineal. se ejecutarán a través del bucle de eventos.
Si está dispuesto a eliminar el primer nivel de sangría alrededor de los argumentos de los métodos asíncronos, no tiene sangría adicional frente a Co-Rutinas y solo un número menor de líneas adicionales de function(callback) {
declaraciones, como esta:
var async = require(''async'');
var someArray = [1, 2, 3, 4, 5, 6, 7, 8, 9];
async.forEach(someArray,
function(number, callback) {
//Do something with the number
callback();
}, function(err) {
//Done doing stuff, or one of the calls to the previous function returned an error I need to deal with
});
En este caso, usted sabe que todas las variables que utiliza su código solo se podrían haber cambiado antes de que se ejecute su código si no fueron modificadas por su código, por lo que puede hacer más fácil la depuración y solo hay un mecanismo de "devolución" : callback()
. O bien devuelve la llamada sin éxito o pasa un error al devolver la llamada cuando algo sale mal.
Reutilización de código no difícil
El ejemplo anterior hace que la reutilización del código sea difícil pero no tiene que serlo. Siempre se pueden pasar funciones nombradas como parámetros:
var async = require(''async'');
// Javascript doesn''t care about declaration order within a scope,
// so order the declarations in a way that''s most readable to you
async.forEach(someArray, frazzleNumber, doneFrazzling);
var someArray = [1, 2, 3, 4, 5, 6, 7, 8, 9];
function frazzleNumber(number, callback) {
// Do something to number
callback();
}
function doneFrazzling(err) {
// Do something or handle error
}
Funcional, no imperativo.
El módulo asíncrono desalienta el uso del control de flujo de estilo imperativo y fomenta (requiere, para las partes que cruzan el bucle de eventos) el uso de funciones para el control de flujo.
La ventaja del estilo funcional es que puede reutilizar fácilmente el cuerpo de su bucle o su condicional, y puede crear nuevos "verbos" de flujo de control que se correspondan mejor con el flujo de su código (demostrado por la existencia misma del biblioteca asíncrona), como el método de flujo de control async.auto
que implementa la resolución del gráfico de dependencia para el orden de llamada de función. (Especifica una serie de funciones nombradas y enumera las otras funciones, si las hay, de las que depende su ejecución, y ejecuta auto
funciones "independientes" y luego la siguiente función que puede ejecutarse en función de cuándo hayan terminado de ejecutarse las funciones dependientes. )
En lugar de escribir su código para ajustarse al estilo imperativo dictado por su idioma, escriba su código como lo dicta la lógica del problema e implemente el flujo de control de "pegamento" para que suceda.
En resumen
La fibra, por su propia naturaleza de extender el lenguaje Javascript, no puede desarrollar un gran ecosistema dentro de Node.js, especialmente cuando Async obtiene el 80% del departamento de apariencia, y no tiene ninguna de las otras desventajas de las co-rutinas en Javascript.
También debe retirar Step .
Maneja solo un pequeño subconjunto de lo que puede hacer async, pero creo que el código es mucho más fácil de leer. Es genial solo para manejar el caso normal de hacer una secuencia de cosas, con algunas de esas cosas sucediendo en paralelo.
Tiendo a usar Step para la mayor parte de mi lógica, y luego uso async ocasionalmente cuando necesito aplicar los métodos repetidamente en ejecución en serie o en paralelo (es decir, llamar a esta función hasta, o llamar a esta función en cada elemento de esta matriz).