¿Cómo se hace una función asíncrona sincrónica?

En realidad, no puede hacer que las funciones asincrónicas parezcan sincrónicas sin algo como hilos / fibras / corutinas. Node.js no tiene ninguno de estos expuestos a JavaScript.

ACTUALIZACIÓN: Los generadores de ES6 y los asíncronos / en espera de ES7 (ambos esencialmente corutinas) hacen posible este tipo de cosas y debería considerar usarlas. Ver EDIT2 a continuación.

Las funciones asincrónicas suelen tener algún tipo de argumento de devolución de llamada, que es donde desea colocar cualquier código que deba ejecutarse después de que se haya completado. Por ejemplo, su función “foo” tendría un argumento de devolución de llamada:

foo(function() { bar(); }) 

Si no es necesario pasar argumentos, esto podría simplificarse a:

foo(bar);

La implementación de “foo” podría verse así:

 function foo(callback) { doAsyncStuff(function(result) { callback(); }); // or simply "doAsyncStuff(callback);" but "result" would get passed to the callback } 

En el caso de llamar a “foo (bar)”, el argumento de “devolución de llamada” dentro de esta función se refiere a la función “bar”. Se llama cada vez que se completa la función doAsyncStuff.

EDITAR: node-fibres expone la fibra primitiva en Node.js

EDIT2: ES6 tiene generadores, que se pueden usar junto con las promesas de usar construcciones de flujo de control tradicionales (if, for, while, try / catch, etc.) de forma natural con funciones asincrónicas.

Ver Generadores vs Fibras petkaantonov / bluebird kriskowal / q tj / co o http://taskjs.org/

 var add2AsyncThings = Bluebird.coroutine(function *() { try { var a = yield someAsyncFunction(); var b = yield anotherAsyncFunction(); return a + b; } catch(e) { // rejected promise from either function is caught here } }) 

ES7 probablemente tendrá funciones “asíncronas” y la sintaxis “espera” para hacer esto aún más fácil:

 asyn function add2AsyncThings() { try { var a = await someAsyncFunction(); var b = await anotherAsyncFunction(); return a + b; } catch(e) { // rejected promise from either function is caught here } }) 

Consulte las funciones asíncronas de ES7 o lukehoban / ecmascript-asyncawait

Tanto los generadores como async / await pueden funcionar en navegadores o Node.js hoy a través de un transpilador como Babel, Regenerator o Traceur.

Aunque podría hacerlo encadenando devoluciones de llamada, una buena biblioteca puede hacer que las cosas sean más manejables, especialmente si las cosas se complican más.

Echa un vistazo al módulo de nodo asíncrono . Sus herramientas le permiten crear fácilmente diferentes patrones de funciones asincrónicas. En su ejemplo, podría usar la función de serie.

https://github.com/caolan/async/

Lo que has escuchado sobre los generadores en realidad tiene que ver más o menos con la propuesta asíncrona / espera para ES7, que está trayendo al lenguaje algunos trucos que usan generadores para que sea agradable escribir código asincrónico de una manera que se parezca más a la sincronización código.

Así es como se ve una función asíncrona, como se propone:

 async function chainAnimationsAsync(elem, animations) { var ret = null; for(var anim of animations) { ret = await anim(elem); } return ret; } 

El código anterior toma un elemento y una matriz de funciones de animación, y llama a las animaciones en orden secuencialmente y luego regresa.

Es posible que hayas visto el palabra clave await : lo que está haciendo (de manera muy simple) es generar una Promesa (y pausar la función asincrónica), y luego, cuando se resuelve esa promesa, la función asincrónica se reanuda hasta que vuelva a esperar o regrese.

La función anterior se puede reescribir usando generadores ES6 de la siguiente manera:

 function chainAnimationsGenerator(elem, animations) { return spawn(function*() { var ret = null; for(var anim of animations) { ret = yield anim(elem); } return ret; }); } 

La función de generación (que pondré a continuación) maneja las Promesas producidas por la función del generador interno y reanuda la función cuando se resuelven.

 function spawn(genF) { return new Promise(function(resolve, reject) { var gen = genF(); function step(nextF) { var next; try { next = nextF(); } catch(e) { // finished with failure, reject the promise return reject(e); } if(next.done) { // finished with success, resolve the promise return resolve(next.value); } // not finished, chain off the yielded promise and `step` again Promise.cast(next.value).then(function(v) { step(function() { return gen.next(v); }); }, function(e) { step(function() { return gen.throw(e); }); }); } step(function() { return gen.next(undefined); }); }); } 

Entonces, utilizando generadores y la función de generación, puede escribir código asincrónico (donde las funciones asincrónicas devuelven promesas) de una manera que se vea y pueda ser tratada secuencialmente.

Una forma simple de hacer que las funciones asincrónicas se ejecuten sincrónicamente es a través de la anidación.

función foo (parámetros, devolución de llamada) {
// hacer cosas con params

// pasa al siguiente paso
devolución de llamada (params)
}

barra de funciones (parámetros, devolución de llamada) {
// hacer cosas con params

// pasa al siguiente paso
devolución de llamada (params)
}

foo (1, bar); // foo se ejecutará, luego llama a bar después de que foo haya terminado.

Ahora, puede ser que el foo mismo espere algo. Entonces debe pasar la devolución de llamada de foo a esa función. El código secuencial / síncrono de larga ejecución hecho de esta manera puede volverse desordenado y feo. La mayoría de las veces, si estás haciendo esto mucho, no estás aprovechando al máximo el sistema asincrónico; en otras palabras, lo estás haciendo mal.

Un nido de código de larga duración significa que está pasando el estado a lo largo de una cadena de código; esto no escalará muy bien porque tiene que ejecutarse secuencialmente y no puede ser paralelo.