Considere el siguiente código que lee una matriz de archivos de manera serial / secuencial. readFiles
devuelve una promesa, que se resuelve solo una vez que todos los archivos se han leído en secuencia.
var readFile = function(file) {
... // Returns a promise.
};
var readFiles = function(files) {
return new Promise((resolve, reject) =>
var readSequential = function(index) {
if (index >= files.length) {
resolve();
} else {
readFile(files[index]).then(function() {
readSequential(index + 1);
}).catch(reject);
}
};
readSequential(0); // Start!
});
};
El código anterior funciona, pero no me gusta tener que recurrir para que las cosas ocurran secuencialmente. ¿Hay alguna manera más simple de que este código se pueda volver a escribir para que no tenga que usar mi extrañoreadSequential
función?
Originalmente intenté usarlo Promise.all
, pero eso provocó que todas las readFile
llamadas tuvieran lugar simultáneamente, lo cual no es lo que quiero:
var readFiles = function(files) {
return Promise.all(files.map(function(file) {
return readFile(file);
}));
};
javascript
promise
q
sequential
serial-processing
XåpplI'-I0llwlg'I -
fuente
fuente
readFileSequential()
ya ha regresado antes de que se llame al siguiente (porque es asíncrono, se completa mucho después de que la llamada a la función original ya haya regresado).Respuestas:
Actualización 2017 : usaría una función asíncrona si el entorno lo admite:
Si lo desea, puede aplazar la lectura de los archivos hasta que los necesite utilizando un generador asíncrono (si su entorno lo admite):
Actualización: Pensándolo bien, podría usar un bucle for en su lugar:
O más compacto, con reducción:
En otras bibliotecas prometedoras (como when y Bluebird) tiene métodos de utilidad para esto.
Por ejemplo, Bluebird sería:
Aunque realmente no hay razón para no usar async, aguarde hoy.
fuente
Promise.resolve(Promise.resolve(15))
es idéntico alPromise.resolve(15)
.Así es como prefiero ejecutar tareas en serie.
fuente
result = result.then(task);
Esta pregunta es antigua, pero vivimos en un mundo de ES6 y JavaScript funcional, así que veamos cómo podemos mejorar.
Debido a que las promesas se ejecutan de inmediato, no podemos simplemente crear una serie de promesas, todas se dispararían en paralelo.
En cambio, necesitamos crear una serie de funciones que devuelva una promesa. Cada función se ejecutará secuencialmente, lo que iniciará la promesa en su interior.
Podemos resolver esto de varias maneras, pero mi forma favorita es usar
reduce
.Se vuelve un poco complicado usarlo
reduce
en combinación con las promesas, por lo que he dividido el revestimiento en algunas picaduras más pequeñas que se pueden digerir a continuación.La esencia de esta función es usarla
reduce
comenzando con un valor inicial dePromise.resolve([])
, o una promesa que contenga una matriz vacía.Esta promesa se pasará al
reduce
método comopromise
. Esta es la clave para encadenar cada promesa en forma secuencial. La próxima promesa de ejecución esfunc
y cuando sethen
dispara, los resultados se concatenan y esa promesa se devuelve, ejecutando elreduce
ciclo con la siguiente función de promesa.Una vez que se hayan ejecutado todas las promesas, la promesa devuelta contendrá una serie de todos los resultados de cada promesa.
Ejemplo ES6 (un revestimiento)
Ejemplo ES6 (desglosado)
Uso:
fuente
Array.prototype.concat.bind(result)
es la parte que me faltaba, tenía que empujar a los resultados manualmente que funcionó pero fue menos genialconsole.log.bind(console)
declaración en su último ejemplo ahora es generalmente innecesaria. En estos días puedes pasarconsole.log
. P.ej.serial(funcs).then(console.log)
. Probado en nodejs actuales y Chrome.Promise.resolve([]).then((x) => { const data = mockApi('/data/1'); return Promise.resolve(x.concat(data)) }).then((x) => { const data = mockApi('/data/2'); return Promise.resolve(x.concat(data)); });
Promise.resolve([]).then(x => someApiCall('url1').then(r => x.concat(r))).then(x => someApiCall('url2').then(r => x.concat(r)))
y así sucesivamentePara hacer esto simplemente en ES6:
fuente
files.forEach
si los archivos son una matriz.for (file of files) {...}
.Promise.resolve()
para crear una promesa ya resuelta en la vida real. Por qué no?Promise.resolve()
parece más limpio quenew Promise(success => success())
.Promise.resolve();
en su código.return sequence;
pusesequence.then(() => { do stuff });
Util simple para la promesa estándar de Node.js:
ACTUALIZAR
items-promise es un paquete NPM listo para usar que hace lo mismo.
fuente
Tuve que ejecutar muchas tareas secuenciales y usé estas respuestas para forjar una función que se encargaría de manejar cualquier tarea secuencial ...
La función toma 2 argumentos + 1 opcional. El primer argumento es la matriz en la que estaremos trabajando. El segundo argumento es la tarea en sí, una función que devuelve una promesa, la siguiente tarea se iniciará solo cuando esta promesa se resuelva. El tercer argumento es una devolución de llamada para ejecutarse cuando se hayan realizado todas las tareas. Si no se pasa la devolución de llamada, la función devuelve la promesa que creó para que podamos manejar el final.
Aquí hay un ejemplo de uso:
Espero que ahorre algo de tiempo a alguien ...
fuente
La mejor solución que pude resolver fue con
bluebird
promesas. Simplemente puede hacer loPromise.resolve(files).each(fs.readFileAsync);
que garantiza que las promesas se resuelvan secuencialmente en orden.fuente
Promise.each(filtes, fs.readFileAsync)
. Por cierto, no tienes que hacer.bind(fs)
?new Array(int)
. Todo lo que hace es preestablecer ellength
par clave-valor, afectando cuántos índices se usan durante la iteración basada en la longitud. Tiene cero efecto en la indexación o límites del índice de la matriz real)Esta es una ligera variación de otra respuesta anterior. Usando promesas nativas:
Explicación
Si tienes estas tareas
[t1, t2, t3]
, entonces lo anterior es equivalente aPromise.resolve().then(t1).then(t2).then(t3)
. Es el comportamiento de reducir.Cómo utilizar
Primero necesitas construir una lista de tareas! Una tarea es una función que no acepta argumentos. Si necesita pasar argumentos a su función, utilice
bind
u otros métodos para crear una tarea. Por ejemplo:fuente
Mi solución preferida:
No es fundamentalmente diferente de otros publicados aquí, pero:
Ejemplo de uso:
Probado en Chrome actual razonable (v59) y NodeJS (v8.1.2).
fuente
¡Úselo
Array.prototype.reduce
y recuerde incluir sus promesas en una función, de lo contrario ya se estarán ejecutando!agradable y fácil ... debería poder reutilizar la misma semilla para el rendimiento, etc.
Es importante protegerse contra las matrices vacías o las matrices con solo 1 elemento cuando se utiliza reducir , por lo que esta técnica es su mejor opción:
y luego lo llaman así:
fuente
Creé este método simple en el objeto Promise:
Cree y agregue un método Promise.sequence al objeto Promise
Uso:
Lo mejor de esta extensión del objeto Promise es que es coherente con el estilo de las promesas. Promise.all y Promise.sequence se invocan de la misma manera, pero tienen una semántica diferente.
Precaución
La ejecución secuencial de promesas no suele ser una muy buena forma de utilizarlas. Por lo general, es mejor usar Promise.all y dejar que el navegador ejecute el código lo más rápido posible. Sin embargo, existen casos de uso reales, por ejemplo, al escribir una aplicación móvil con javascript.
fuente
Promise.all
y tuPromise.sequence
. Uno toma una iteración de promesas, el otro toma una serie de funciones que devuelven promesas.reduce
gusta en la respuesta de Benjamin es mucho más simple.Puede usar esta función que se promete Lista de fábricas:
Promise Factory es simplemente una función simple que devuelve una Promesa:
Funciona porque una fábrica de promesas no crea la promesa hasta que se le pide. Funciona de la misma manera que una función then; de hecho, ¡es lo mismo!
No desea operar sobre una serie de promesas en absoluto. Según la especificación Promesa, tan pronto como se crea una promesa, comienza a ejecutarse. Entonces, lo que realmente quieres es una variedad de fábricas prometedoras ...
Si desea obtener más información sobre Promesas, debe consultar este enlace: https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
fuente
Mi respuesta está basada en https://stackoverflow.com/a/31070150/7542429 .
Esta solución devuelve los resultados como una matriz como Promise.all ().
Uso:
fuente
Realmente me gustó la respuesta de @ joelnet, pero para mí, ese estilo de codificación es un poco difícil de digerir, así que pasé un par de días tratando de descubrir cómo expresaría la misma solución de una manera más legible y esta es mi tomar, solo con una sintaxis diferente y algunos comentarios.
fuente
Como Bergi notó, creo que la solución mejor y clara es usar BlueBird, cada uno, el código a continuación:
fuente
Primero, debe comprender que una promesa se ejecuta en el momento de la creación.
Entonces, por ejemplo, si tiene un código:
Necesitas cambiarlo a:
Entonces necesitamos encadenar las promesas secuencialmente:
ejecutando
after()
, se asegurará de que la promesa se cree (y ejecute) solo cuando llegue el momento.fuente
Yo uso el siguiente código para extender el objeto Promise. Maneja el rechazo de las promesas y devuelve una serie de resultados.
Código
Ejemplo
fuente
Si lo desea, puede usar reducir para hacer una promesa secuencial, por ejemplo:
siempre funcionará en secuencia.
fuente
Usando ES moderno:
fuente
Con Async / Await (si tiene el soporte de ES7)
(debes usar
for
loop, y noforEach
porque async / await tenga problemas para ejecutarse en cada bucle)Sin Async / Await (usando Promise)
fuente
forEach
(según esto )Sobre la base del título de la pregunta, "¿Resolver promesas una tras otra (es decir, en secuencia)?", Podríamos entender que el OP está más interesado en el manejo secuencial de las promesas de liquidación que las llamadas secuenciales per se .
Esta respuesta se ofrece:
Si realmente no se desean llamadas concurrentes, vea la respuesta de Benjamin Gruenbaum que cubre las llamadas secuenciales (etc.) de manera integral.
Sin embargo, si está interesado (para mejorar el rendimiento) en patrones que permiten llamadas simultáneas seguidas de un manejo secuencial de las respuestas, siga leyendo.
Es tentador pensar que tiene que usar
Promise.all(arr.map(fn)).then(fn)
(como lo he hecho muchas veces) o un azúcar elegante de Promise lib (especialmente Bluebird), sin embargo (con crédito a este artículo ) unarr.map(fn).reduce(fn)
patrón hará el trabajo, con las ventajas de que:.then()
se usa.Aquí está, escrito para
Q
.Nota: solo ese fragmento,
Q()
es específico de Q. Para jQuery, debe asegurarse de que readFile () devuelva una promesa de jQuery. Con las bibliotecas A +, las promesas extranjeras se asimilarán.La clave aquí es la
sequence
promesa de la reducción , que secuencia el manejo delreadFile
promesas pero no su creación.Y una vez que haya absorbido eso, ¡tal vez sea un poco alucinante cuando se dé cuenta de que el
.map()
escenario no es realmente necesario! Todo el trabajo, llamadas paralelas más manejo en serie en el orden correcto, se puede lograrreduce()
solo, más la ventaja adicional de una mayor flexibilidad para:Aquí está, para
Q
otra vez.Ese es el patrón básico. Si también quisiera entregar datos (por ejemplo, los archivos o alguna transformación de ellos) a la persona que llama, necesitaría una variante leve.
fuente
sequence.then(() => filePromise)
es un antipatrón: no propaga errores tan pronto como podrían (y creaunhandledRejection
en libs que los admiten). Prefieres usarQ.all([sequence, filePromise])
o$.when(sequence, filePromise)
. Es cierto que este comportamiento puede ser lo que desea cuando intenta ignorar u omitir errores, pero al menos debe mencionar esto como una desventaja.unhandledRejection
eventos. En Bluebird puede solucionar este problema utilizando elsequence.return(filePromise)
que tiene el mismo comportamiento pero maneja los rechazos de manera correcta. No conozco ninguna referencia, solo se me ocurrió, no creo que el "(anti) patrón" tenga un nombre todavía.Su enfoque no es malo, pero tiene dos problemas: se traga los errores y emplea el antipatrón de construcción de promesa explícita.
Puede resolver estos dos problemas y hacer que el código sea más limpio, mientras sigue empleando la misma estrategia general:
fuente
Si alguien más necesita una forma garantizada de una forma ESTRICTAMENTE secuencial de resolver Promesas al realizar operaciones CRUD, también puede usar el siguiente código como base.
Siempre que agregue 'return' antes de llamar a cada función, describiendo una Promesa, y use este ejemplo como base, la siguiente llamada a la función .then () comenzará CONSISTENTEMENTE después de completar la anterior:
fuente
El método push y pop de matriz se puede usar para la secuencia de promesas. También puede impulsar nuevas promesas cuando necesite datos adicionales. Este es el código que usaré en el cargador React Infinite para cargar la secuencia de páginas.
fuente
La mayoría de las respuestas no incluyen los resultados de TODAS las promesas individualmente, por lo que en caso de que alguien esté buscando este comportamiento en particular, esta es una posible solución mediante la recursividad.
Sigue el estilo de
Promise.all
:Devuelve la matriz de resultados en la
.then()
devolución de llamada.Si alguna promesa falla, se devuelve inmediatamente en la
.catch()
devolución de llamada.Nota sobre la
tasks
declaración de matriz :En este caso no es posible usar la siguiente notación como
Promise.all
usaría:Y tenemos que usar:
La razón es que JavaScript comienza a ejecutar la promesa inmediatamente después de su declaración. Si usamos métodos como
Promise.all
, solo verifica que el estado de todos ellos seafulfilled
orejected
, pero no inicia la exención en sí. Usando() => promise()
detenemos la ejecución hasta que se llame.fuente
Aquí la clave es cómo llamar a la función dormir. Debe pasar una serie de funciones que en sí devuelve una promesa en lugar de una serie de promesas.
fuente
Esto es para ampliar cómo procesar una secuencia de promesas de una manera más genérica, admitiendo secuencias dinámicas / infinitas, basadas en la implementación de secuencia spex :
No solo esta solución funcionará con secuencias de cualquier tamaño, sino que puede agregarle fácilmente aceleración de datos y equilibrio de carga .
fuente