Ejecutar funciones asíncronas en serie con promises

Estoy tratando de ejecutar múltiples tareas asincrónicas en serie utilizando promises. Cada tarea debería ejecutarse justo después de que termine la anterior. Esto es un ejemplo simplificado de lo que he intentado:

var order = []; var tasks = [ new Promise(resolve => { order.push(1); setTimeout(() => { order.push(2) resolve(); }, 100); }), new Promise(resolve => { order.push(3); setTimeout(() => { order.push(4) resolve(); }, 100); }), new Promise(resolve => { order.push(5); resolve(); }) ]; tasks.networkinguce((cur, next) => cur.then(next), Promise.resolve()).then(() => { console.log(order); // [ 1, 3, 5 ] }); setTimeout(() => console.log(order), 200); // [ 1, 3, 5, 2, 4 ] 

Esperaría que el order sea ​​igual [ 1, 2, 3, 4, 5 ] en la function de callback. Sin embargo, obtuve esos extraños resultados ( [ 1, 3, 5 ] en la callback y [ 1, 3, 5, 2, 4 ] en la function retardada). ¿Qué me estoy perdiendo?

Cuando escribes algo así como

 new Promise(resolve => { order.push(1); setTimeout(() => { order.push(2) resolve(); }, 100); }); 

se ejecuta de inmediato, lo que significa que se ejecuta ahora y se resuelve en 0.1 segundos.
No importa que lo escriba dentro de una matriz, las funciones todavía se ejecutan en este momento , y las promises se devuelven como los valores en la matriz.

En otras palabras, las tres llamadas prometedoras se ejecutan en paralelo, todas se ejecutan de inmediato, con solo milisegundos de diferencia, y se resuelven en el momento dado en el timer interno, a partir de ahora .

Si quieres ejecutar una promise después de la otra, tienen que ser envueltas de alguna manera para que no se ejecuten ahora , pero cada vez que se llaman, por ejemplo algo así como

 var tasks = [ _ => new Promise(resolve => { order.push(1); setTimeout(() => { order.push(2) resolve(); }, 100); }), _ => new Promise(resolve => { order.push(3); setTimeout(() => { order.push(4) resolve(); }, 100); }), _ => new Promise(resolve => { order.push(5); resolve(); }), ]; 

(el guión bajo es una abreviatura válida de ES2015 para una function de flecha anónima)

donde cada valor de matriz es una function anónima a la que se puede llamar, y cuando se llama el constructor de promise se ejecuta y devuelve la promise.

Para llamar recursivamente a las funciones en serie, una llamada de function recursiva es la más fácil, donde se llama a la siguiente function cuando se termina la stream, etc.

 (function iterate(i) { tasks[i]().then(() => { // when done if (tasks[++i]) iterate(i); // call the next one }); })(0); 

VIOLÍN


Editar:

También puede Array.networkinguce la forma en que ya lo está haciendo, ahora que tiene funciones que devuelve promises

 tasks.networkinguce((cur, next) => cur.then(next), Promise.resolve()).then(() => { // all done, chain is complete ! }); 

Esta es una forma de hacer las cosas less prometedora y un poco extraña, pero parece funcionar:

 "use strict"; var order = []; var i = 0; function next(){ if(tasks[++i]) tasks[i]() } var tasks = [ function() { order.push(1); setTimeout(() => { order.push(2) next() }, 100); }, function() { order.push(3); setTimeout(() => { order.push(4) next(); }, 100); }, function() { order.push(5); next() console.log(order) } ]; tasks[0]() 

Te estás perdiendo el hecho de que cuando usas setTimeout , las devoluciones de llamada (que presionan 2 , 4 y order logging) se ejecutarán en la siguiente iteración del ciclo de evento , es decir, el próximo 'tick'. Mientras que todas las otras funciones (los constructores Promise y la callback networkingucida) se están ejecutando inmediatamente , es decir, en el 'tick' actual.

 var order = []; var tasks = [ new Promise(resolve => { order.push(1); // 1. callback executes immediately pushing 1 into order setTimeout(() => { // 2. setTimeout executes, pushing the callback into the event queue after 100ms order.push(2) // 8. callback executes, pushing 2 into order resolve(); }, 100); }), new Promise(resolve => { order.push(3); // 3. callback executes immediately pushing 3 into order setTimeout(() => { // 4. setTimeout executes, pushing the callback into the event queue after 100ms order.push(4) // 9. callback executes, pushing 4 into order resolve(); }, 100); }), new Promise(resolve => { order.push(5); // 5. callback executes immediately pushing 5 into order resolve(); }) ]; console.log(order); // [ 1, 3, 5 ] // 6. networkinguce executes immediately, executes Promise.resolve which logs order and then loops through order and executes the callback everytime tasks.networkinguce((cur, next) => cur.then(next), Promise.resolve()).then(() => { console.log(order); // [ 1, 3, 5 ] }); setTimeout(() => { console.log(order); // 10. callback executes and logs order }, 200); // 7. setTimeout executes, pushing the callback into the event queue after 200ms 

Solo después de que se produzcan los pasos 1 a 7, se ejecutarán todas las devoluciones de llamada en queue de events (por setTimeout ), es decir, presionadas en la stack de llamadas, despejando la queue de events y finalmente borrando la stack de llamadas, después de ejecutar dichas devoluciones de llamadas (pasos 8 , 9 y 10).

Tenga en count que las funciones solo se quitan de la queue de events cuando la stack de llamadas está vacía.

Cada function asincrónica tiene una parte síncrona, la configuration que conduce al retorno (sincrónico) de la promise.