¿Cómo puedo ejecutar la matriz de promesas en orden secuencial?


Tengo una serie de promesas que deben ejecutarse en orden secuencial.

var promises = [promise1, promise2, ..., promiseN];

Llamando a RSVP.todos los ejecutarán 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 la matriz de promesas se construye dinámicamente.

Author: jakub.g, 2013-11-20

7 answers

Si ya los tiene en una matriz, entonces ya se están ejecutando. Si tienes una promesa, entonces ya se está ejecutando. Esto no es una preocupación de las promesas (es decir, no son como C# Tasks en ese sentido con el método .Start()). .all no ejecuta nada sólo devuelve una promesa.

Si tiene una matriz de funciones promise returning:

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
});
 116
Author: Esailija,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-12 06:44:01

Con las funciones async de ECMAScript 2017 se haría así:

async function executeSequentially() {
    const tasks = [fn1, fn2, fn3]

    for (const fn of tasks) {
        await fn()
    }
}

Puedes usar BabelJS para usar funciones asíncronas ahora

 13
Author: Ujeenator,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-03 16:25:44

Un segundo intento de una respuesta en la que trato de ser más explicativo:

Primero, algunos antecedentes requeridos, del README RSVP:

La parte realmente impresionante viene cuando devuelves una promesa del primer manejador...Esto le permite aplanar las devoluciones de llamada anidadas, y es la característica principal de promises que evita la "deriva hacia la derecha" en programas con mucho código asíncrono.

Así es precisamente como haces promesas secuenciales, regresando la promesa posterior de la then de la promesa que debe terminar antes de ella.

Es útil pensar en un conjunto de promesas como un árbol, donde las ramas representan procesos secuenciales, y las hojas representan procesos simultáneos.

El proceso de construir tal árbol de promesas es análogo a la tarea muy común de construir otros tipos de árboles: mantener un puntero o referencia a dónde en el árbol está agregando ramas actualmente, y agregar iterativamente cosa.

Como @Esailija señaló en su respuesta, si usted tiene una serie de funciones de retorno de promesas que no toman argumentos, puede usar reduce para construir cuidadosamente el árbol para usted. Si alguna vez has implementado reduce por ti mismo, entenderás que lo que reduce está haciendo 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 su then.

Si no tienes una buena variedad de homogénea (con respecto a los argumentos que se tome/retorno) promesa de regresar funciones, o si usted necesita más estructura complicada que una simple secuencia lineal, se puede construir el árbol de las promesas a ti mismo por mantener una referencia a la posición en la promesa de árbol 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 concurrentes y secuenciales utilizando RSVP.todo para añadir múltiples " hojas "a una promesa"rama". Mi la respuesta con voto negativo por ser demasiado complicada muestra un ejemplo de eso.

También puedes usar Ember.ejecutar.ScheduleOnce ('afterRender') para asegurarse de que algo hecho en una promesa se renderiza antes de que se lance la siguiente promesa my mi respuesta votada en contra por ser demasiado complicada también muestra un ejemplo de eso.

 4
Author: Michael Johnston,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-20 22:46:56

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 devuelven Promise.

Ejemplo de émbolo con el código anterior: http://plnkr.co/edit/UP0rhD?p=preview

 3
Author: allenhwkim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-18 15:14:59

Voy a dejar esta respuesta aquí porque esto me habría ayudado cuando vine a buscar aquí una solución a mi problema.

Lo que buscaba era esencialmente mapSeries... y resulta que estoy mapeando guardar sobre un conjunto de valores... y quiero los resultados...

Así que, esto es lo que tengo, FWIW, para ayudar a otros que buscan 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);
    }
});
 0
Author: Julian Leviston,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-26 07:12:30

Todo lo que se necesita para resolver es un bucle for:)

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);
  });
}
 0
Author: Paweł,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-29 04:14:03

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 la salida 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([]);
};
 0
Author: mrded,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-29 10:58:18