web-dev-qa-db-fra.com

Comment synchroniser une séquence de promesses?

J'ai un tableau d'objets de promesse qui doivent être résolus dans la même séquence dans laquelle ils sont répertoriés, c'est-à-dire que nous ne pouvons pas tenter de résoudre un élément tant que le précédent n'a pas été résolu (comme le fait la méthode Promise.all([...])).

Et si un élément est rejeté, j'ai besoin que la chaîne le rejette immédiatement, sans tenter de résoudre l'élément suivant.

Comment puis-je implémenter ceci, ou existe-t-il une implémentation existante pour ce modèle sequence?

function sequence(arr) {
    return new Promise(function (resolve, reject) {
        // try resolving all elements in 'arr',
        // but strictly one after another;
    });
}

MODIFIER

Les réponses initiales suggèrent que nous ne pouvons que sequence résultats de ces éléments de tableau, et non leur exécution, car ils sont prédéfinis dans un tel exemple.

Mais alors, comment générer un tableau de promesses de manière à éviter une exécution précoce?

Voici un exemple modifié:

function sequence(nextPromise) {
    // while nextPromise() creates and returns another promise,
    // continue resolving it;
}

Je ne voudrais pas en faire une question distincte, car je pense que cela fait partie du même problème.

SOLUTION

Certaines réponses ci-dessous et les discussions qui ont suivi se sont égarées un peu, mais la solution éventuelle qui a fait exactement ce que je cherchais a été mise en œuvre dans spex library, en tant que méthode sequence . La méthode peut parcourir une séquence de longueur dynamique et créer des promesses selon les exigences de la logique applicative de votre application.

Plus tard, je l'ai transformée en une bibliothèque partagée accessible à tous.

49
vitaly-t

Les promesses représentent les valeurs des opérations et non les opérations elles-mêmes. Les opérations sont déjà commencées vous ne pouvez donc pas les faire attendre les unes les autres.

Au lieu de cela, vous pouvez synchroniser des fonctions qui renvoient des promesses en les invoquant dans l’ordre (via une boucle avec chaînage de promesses par exemple), ou en utilisant la méthode .each dans Bluebird.

10
Benjamin Gruenbaum

Vous ne pouvez pas simplement exécuter X opérations asynchrones pour ensuite les résoudre dans un ordre.

La bonne façon de procéder est de n’exécuter la nouvelle opération asynchrone qu’après la résolution de la précédente:

doSomethingAsync().then(function(){
   doSomethingAsync2().then(function(){
       doSomethingAsync3();
       .......
   });
});

Modifier
On dirait que vous voulez attendre toutes les promesses, puis invoquer leurs rappels dans un ordre spécifique. Quelque chose comme ça:

var callbackArr = [];
var promiseArr = [];
promiseArr.Push(doSomethingAsync());
callbackArr.Push(doSomethingAsyncCallback);
promiseArr.Push(doSomethingAsync1());
callbackArr.Push(doSomethingAsync1Callback);
.........
promiseArr.Push(doSomethingAsyncN());
callbackArr.Push(doSomethingAsyncNCallback);

et alors:

$.when(promiseArr).done(function(promise){
    while(callbackArr.length > 0)
    {
       callbackArr.pop()(promise);
    }
});

Les problèmes qui peuvent survenir avec cela surviennent lorsqu'une ou plusieurs promesses échouent.

5
Amir Popovich

Bien que relativement dense, voici une autre solution permettant d'itérer une fonction de retour de promesse sur un tableau de valeurs et de le résoudre avec un tableau de résultats:

function processArray(arr, fn) {
    return arr.reduce(
        (p, v) => p.then((a) => fn(v).then(r => a.concat([r]))),
        Promise.resolve([])
    );
}

Usage:

const numbers = [0, 4, 20, 100];
const multiplyBy3 = (x) => new Promise(res => res(x * 3));

// Prints [ 0, 12, 60, 300 ]
processArray(numbers, multiplyBy3).then(console.log);

Notez que, comme nous réduisons d’une promesse à l’autre, chaque élément est traité en série.

Il est fonctionnellement équivalent à la solution "Itération avec .reduce () qui résout avec tableau" de @ jfriend00 mais un peu plus ordonnée.

3
Molomby

Je suppose que deux approches pour traiter cette question:

  1. Créez plusieurs promesses et utilisez la fonction allWithAsync comme suit:
let allPromiseAsync = (...PromisesList) => {
return new Promise(async resolve => {
    let output = []
    for (let promise of PromisesList) {
        output.Push(await promise.then(async resolvedData => await resolvedData))
        if (output.length === PromisesList.length) resolve(output)
    }
}) }
const prm1= Promise.resolve('first');
const prm2= new Promise((resolve, reject) => setTimeout(resolve, 2000, 'second'));
const prm3= Promise.resolve('third');

allPromiseAsync(prm1, prm2, prm3)
    .then(resolvedData => {
        console.log(resolvedData) // ['first', 'second', 'third']
    });
  1. Utilisez plutôt la fonction Promise.all:
  (async () => {
  const promise1 = new Promise(resolve => {
    setTimeout(() => { resolve() }, 2500)
  })

  const promise2 = new Promise(resolve => {
    setTimeout(() => { resolve() }, 5000)
  })

  const promise3 = new Promise(resolve => {
    setTimeout(() => { resolve() }, 1000)
  })

  const promises = [promise1, promise2, promise3]

  await Promise.all(promises)

  console.log('This line is shown after 8500ms')
})()
0
Iman Bahrampour