Tengo una serie de promesas que deben ejecutarse en orden secuencial.
var promises = [promise1, promise2, ..., promiseN];
Llamar a RSVP.all los ejecutará en paralelo:
RSVP.all(promises).then(...);
Pero, ¿cómo puedo ejecutarlos en secuencia?
Puedo apilarlos manualmente así
RSVP.resolve()
.then(promise1)
.then(promise2)
...
.then(promiseN)
.then(...);
pero el problema es que el número de promesas varía y el conjunto de promesas se construye dinámicamente.
javascript
ember.js
promise
rsvp.js
jaaksarv
fuente
fuente
Respuestas:
Si ya los tiene en una matriz, entonces ya se están ejecutando. Si tiene una promesa, entonces ya se está ejecutando. Esto no es una preocupación de las promesas (es decir, no son como C #
Task
s en ese sentido con el.Start()
método)..all
no ejecuta nada, solo devuelve una promesa.Si tiene una serie de funciones de devolución de promesa:
var tasks = [fn1, fn2, fn3...]; tasks.reduce(function(cur, next) { return cur.then(next); }, RSVP.resolve()).then(function() { //all executed });
O valores:
var idsToDelete = [1,2,3]; idsToDelete.reduce(function(cur, next) { return cur.then(function() { return http.post("/delete.php?id=" + next); }); }, RSVP.resolve()).then(function() { //all executed });
fuente
.then
, en este ejemplo simplemente se ignora ...Con las funciones asíncronas de ECMAScript 2017 se haría así:
async function executeSequentially() { const tasks = [fn1, fn2, fn3] for (const fn of tasks) { await fn() } }
Puede usar BabelJS para usar funciones asíncronas ahora
fuente
fn1, fn2, fn3
aquí estén las funciones, por ejemplo() => yourFunctionReturningAPromise()
, en lugar de soloyourFunctionReturningAPromise()
. Esta es también la razón por la queawait fn()
es necesario en lugar de simplementeawait fn
. Vea más en los documentos oficiales . Perdón por publicar como comentario, pero la cola de edición está llena :)ES7 camino en 2017.
<script> var funcs = [ _ => new Promise(resolve => setTimeout(_ => resolve("1"), 1000)), _ => new Promise(resolve => setTimeout(_ => resolve("2"), 1000)), _ => new Promise(resolve => setTimeout(_ => resolve("3"), 1000)), _ => new Promise(resolve => setTimeout(_ => resolve("4"), 1000)), _ => new Promise(resolve => setTimeout(_ => resolve("5"), 1000)), _ => new Promise(resolve => setTimeout(_ => resolve("6"), 1000)), _ => new Promise(resolve => setTimeout(_ => resolve("7"), 1000)) ]; async function runPromisesInSequence(promises) { for (let promise of promises) { console.log(await promise()); } } </script> <button onClick="runPromisesInSequence(funcs)">Do the thing</button>
Esto ejecutará las funciones dadas secuencialmente (una por una), no en paralelo. El parámetro
promises
es una matriz de funciones, que regresanPromise
.Ejemplo de Plunker con el código anterior: http://plnkr.co/edit/UP0rhD?p=preview
fuente
Un segundo intento de respuesta en el que trato de ser más explicativo:
Primero, algunos antecedentes necesarios, del README RSVP :
Así es precisamente como haces promesas secuenciales, devolviendo la promesa posterior del
then
de la promesa que debería terminar antes.Es útil pensar en un conjunto de promesas como un árbol, donde las ramas representan procesos secuenciales y las hojas representan procesos concurrentes.
El proceso de construir un árbol de promesas de este tipo es análogo a la tarea muy común de construir otros tipos de árboles: mantenga un puntero o referencia al lugar del árbol en el que está agregando ramas actualmente y agregue cosas de forma iterativa.
Como @Esailija señaló en su respuesta, si tiene una variedad de funciones de devolución de promesas que no aceptan argumentos, puede usar
reduce
para construir ordenadamente el árbol para usted. Si alguna vez ha implementado reduce por sí mismo, comprenderá que lo que reduce detrás de escena en la respuesta de @ Esailija es mantener una referencia a la promesa actual (cur
) y hacer que cada promesa devuelva la siguiente promesa en suthen
.Si NO tiene una buena matriz de funciones de devolución de promesas homogéneas (con respecto a los argumentos que toman / devuelven), o si necesita una estructura más complicada que una secuencia lineal simple, puede construir el árbol de promesas usted mismo manteniendo una referencia a la posición en el árbol de promesas donde desea agregar nuevas promesas:
var root_promise = current_promise = Ember.Deferred.create(); // you can also just use your first real promise as the root; the advantage of // using an empty one is in the case where the process of BUILDING your tree of // promises is also asynchronous and you need to make sure it is built first // before starting it current_promise = current_promise.then(function(){ return // ...something that returns a promise...; }); current_promise = current_promise.then(function(){ return // ...something that returns a promise...; }); // etc. root_promise.resolve();
Puede crear combinaciones de procesos simultáneos y secuenciales utilizando RSVP.all para agregar múltiples "hojas" a una "rama" de promesa. Mi respuesta votada negativamente por ser demasiado complicada muestra un ejemplo de eso.
También puede usar Ember.run.scheduleOnce ('afterRender') para asegurarse de que algo hecho en una promesa se procese antes de que se active la siguiente; mi respuesta votada negativamente por ser demasiado complicada también muestra un ejemplo de eso.
fuente
then
se desea encadenar a través , ha proporcionado mucha información adicional que oculta la respuesta a la pregunta que se hizo.Promise.resolve().then(...).then(...)...
, no es que la matriz estuviera creciendo mientras se ejecutaban las promesas. Por supuesto, ahora todo es discutible.Otro enfoque más es definir una función de secuencia global en el
Promise
prototipo.Promise.prototype.sequence = async (promiseFns) => { for (let promiseFn of promiseFns) { await promiseFn(); } }
Entonces puedes usarlo en cualquier lugar, como
Promise.all()
Ejemplo
const timeout = async ms => new Promise(resolve => setTimeout(() => { console.log("done", ms); resolve(); }, ms) ); // Executed one after the other await Promise.sequence([() => timeout(1000), () => timeout(500)]); // done: 1000 // done: 500 // Executed in parallel await Promise.all([timeout(1000), timeout(500)]); // done: 500 // done: 1000
Descargo de responsabilidad: ¡tenga cuidado al editar prototipos!
fuente
Todo es necesario para resolver eso es un
for
bucle :)var promises = [a,b,c]; var chain; for(let i in promises){ if(chain) chain = chain.then(promises[i]); if(!chain) chain = promises[i](); } function a(){ return new Promise((resolve)=>{ setTimeout(function(){ console.log('resolve A'); resolve(); },1000); }); } function b(){ return new Promise((resolve)=>{ setTimeout(function(){ console.log('resolve B'); resolve(); },500); }); } function c(){ return new Promise((resolve)=>{ setTimeout(function(){ console.log('resolve C'); resolve(); },100); }); }
fuente
if(!chain) chain = promises[i]();
tiene un()
al final? Creo que en el caso de que la cadena esté vacía (iteración 0), uno solo querría tener la promesa en bruto, y luego el ciclo puede inyectar cada promesa posterior en la de la cadena.then()
. Entonces, ¿no sería estoif(!chain) chain = promises[i];
? Quizás no he entendido algo aquí.a,b,c
hecho son funciones que devuelven promesas, y no promesas. Entonces lo anterior tiene sentido. Pero, ¿de qué sirve envolver las Promesas de esta manera?Tuve un problema similar e hice una función recursiva que ejecuta funciones una por una secuencialmente.
var tasks = [fn1, fn2, fn3]; var executeSequentially = function(tasks) { if (tasks && tasks.length > 0) { var task = tasks.shift(); return task().then(function() { return executeSequentially(tasks); }); } return Promise.resolve(); };
En caso de que necesite recopilar resultados de estas funciones:
var tasks = [fn1, fn2, fn3]; var executeSequentially = function(tasks) { if (tasks && tasks.length > 0) { var task = tasks.shift(); return task().then(function(output) { return executeSequentially(tasks).then(function(outputs) { outputs.push(output); return Promise.resolve(outputs); }); }); } return Promise.resolve([]); };
fuente
export type PromiseFn = () => Promise<any>; export class PromiseSequence { private fns: PromiseFn[] = []; push(fn: PromiseFn) { this.fns.push(fn) } async run() { for (const fn of this.fns) { await fn(); } } }
entonces
const seq = new PromiseSequence(); seq.push(() => Promise.resolve(1)); seq.push(() => Promise.resolve(2)); seq.run();
también es posible almacenar lo que las promesas regresan en otra var privada y pasarlo a devoluciones de llamada
fuente
Lo que buscaba era esencialmente mapSeries, y resulta que estoy mapeando guardar sobre un conjunto de valores, y quiero los resultados.
Entonces, aquí está todo lo que llegué, para ayudar a otros a buscar cosas similares en el futuro ...
(Tenga en cuenta que el contexto es una aplicación Ember).
App = Ember.Application.create(); App.Router.map(function () { // put your routes here }); App.IndexRoute = Ember.Route.extend({ model: function () { var block1 = Em.Object.create({save: function() { return Em.RSVP.resolve("hello"); }}); var block2 = Em.Object.create({save: function() { return Em.RSVP.resolve("this"); }}); var block3 = Em.Object.create({save: function() { return Em.RSVP.resolve("is in sequence"); }}); var values = [block1, block2, block3]; // want to sequentially iterate over each, use reduce, build an array of results similarly to map... var x = values.reduce(function(memo, current) { var last; if(memo.length < 1) { last = current.save(); } else { last = memo[memo.length - 1]; } return memo.concat(last.then(function(results) { return current.save(); })); }, []); return Ember.RSVP.all(x); } });
fuente