web-dev-qa-db-fra.com

Comment puis-je exécuter un tableau de promesses dans un ordre séquentiel?

J'ai un éventail de promesses qui doivent être exécutées dans un ordre séquentiel.

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

L'appel de RSVP.all les exécutera en parallèle:

RSVP.all(promises).then(...); 

Mais, comment puis-je les exécuter en séquence?

Je peux les empiler manuellement comme ça

RSVP.resolve()
    .then(promise1)
    .then(promise2)
    ...
    .then(promiseN)
    .then(...);

mais le problème est que le nombre de promesses varie et que la gamme de promesses est construite de manière dynamique.

70
jaaksarv

Si vous les avez déjà dans un tableau, ils sont déjà en train de s'exécuter. Si vous avez une promesse, elle est déjà exécutée. Ce n'est pas une préoccupation de promesses (I.E ils ne sont pas comme C # Tasks à cet égard avec la méthode .Start()) .all n'exécute rienit ne retourne qu'une promesse.

Si vous avez un tableau de fonctions de retour de promesse:

var tasks = [fn1, fn2, fn3...];

tasks.reduce(function(cur, next) {
    return cur.then(next);
}, RSVP.resolve()).then(function() {
    //all executed
});

Ou valeurs:

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
});
121
Esailija

Avec les fonctions asynchrones d'ECMAScript 2017, cela se ferait comme suit:

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

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

Vous pouvez utiliser BabelJS pour utiliser les fonctions asynchrones maintenant

15
Ujeenator

ES7 façon 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>

Ceci exécutera les fonctions données séquentiellement (une par une), pas en parallèle. Le paramètre promises est un tableau de fonctions qui retournent Promise.

Exemple avec le code ci-dessus: http://plnkr.co/edit/UP0rhD?p=preview

5
allenhwkim

Une deuxième tentative de réponse dans laquelle j'essaie d'être plus explicatif:

Tout d’abord, quelques informations de base requises dans RSVP README :

La partie vraiment géniale vient quand vous retournez une promesse du premier gestionnaire ... Cela vous permet de réduire les rappels imbriqués, et constitue la principale caractéristique des promesses qui empêche la "dérive vers la droite" dans les programmes avec beaucoup de code asynchrone.

C’est précisément ainsi que vous faites des promesses séquentielles, en retournant la promesse ultérieure de la variable then de la promesse qui devrait se terminer avant celle-ci.

Il est utile de considérer un tel ensemble de promesses comme un arbre, où les branches représentent des processus séquentiels et les feuilles, des processus simultanés.

Le processus de création d’un tel arbre de promesses est analogue à la tâche très courante qui consiste à créer d’autres types d’arbres: maintenez un pointeur ou une référence indiquant où vous ajoutez actuellement des branches dans l’arbre et ajoutez des éléments de manière itérative.

Comme @Esailija l'a souligné dans sa réponse, si vous avez un tableau de fonctions de retour de promesse ne prenant pas d'arguments, vous pouvez utiliser reduce pour construire proprement l'arborescence pour vous. Si vous avez déjà implémenté réduire vous-même, vous comprendrez que réduire @ @ Esailija en coulisse consiste à conserver une référence à la promesse actuelle (cur) et à faire en sorte que chaque promesse retourne la promesse suivante dans sa then.

Si vous N'AVEZ PAS un joli tableau de fonctions de promesse retournées homogènes (en ce qui concerne les arguments qu'ils prennent/retournent), ou si vous avez besoin d'une structure plus compliquée qu'une simple séquence linéaire, vous pouvez construire vous-même l'arbre de promesses en maintenant une référence à la position dans l'arbre de promesse où vous souhaitez ajouter de nouvelles promesses:

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();

Vous pouvez créer des combinaisons de processus simultanés et séquentiels en utilisant RSVP.all pour ajouter plusieurs "feuilles" à une "branche" de promesse. Ma réponse votée pour être trop compliquée montre un exemple de cela.

Vous pouvez également utiliser Ember.run.scheduleOnce ('afterRender') pour vous assurer que quelque chose qui est fait dans une promesse est rendu avant que la promesse suivante ne soit renvoyée - ma réponse votée pour être trop compliqué en donne également un exemple.

4
Michael Johnston

Je vais laisser cette réponse ici parce que cela m'aurait aidé lorsque je suis venu chercher ici une solution à mon problème.

Ce que je recherchais était essentiellement mapSeries ... et il m'est arrivé de mapper des sauvegardes sur un ensemble de valeurs ... et je veux les résultats ...

Donc, voici ce que j'ai eu, FWIW, pour aider les autres à rechercher des choses similaires dans le futur ...

(Notez que le contexte est une application 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
Julian Leviston

Tout est nécessaire pour résoudre cela est une boucle 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
Paweł

J'ai eu un problème similaire, et j'ai créé une fonction récursive qui exécute les fonctions une par une en séquence.

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();  
};

Si vous devez collecter les résultats de ces fonctions:

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
mrded