web-dev-qa-db-fra.com

Est-ce que Promise.all est un traitement natif de Node.js en parallèle ou séquentiel?

Je voudrais clarifier ce point, car la documentation n'est pas très claire à ce sujet;

Q1: Est-ce que Promise.all(iterable) traite toutes les promesses de manière séquentielle ou en parallèle? Ou, plus précisément, est-ce que cela équivaut à exécuter des promesses chaînées comme 

p1.then(p2).then(p3).then(p4).then(p5)....

ou est-ce un autre type d'algorithme où tous p1, p2, p3, p4, p5, etc. sont appelés en même temps (en parallèle) et les résultats sont renvoyés dès que tous ont été résolus (ou rejetés)?

Q2: Si Promise.all est exécuté en parallèle, existe-t-il un moyen pratique d'exécuter un processus séquentiel itérable?

Note: Je ne veux pas utiliser Q ou Bluebird, mais toutes les spécifications natives de l'ES6.

108
Yanick Rochon

Promise.all(iterable) exécute-t-il toutes les promesses?

Non, les promesses ne peuvent pas "être exécutées". Ils commencent leur tâche lorsqu'ils sont créés - ils représentent uniquement les résultats - et vous exécutez tout en parallèle, même avant de les transmettre à Promise.all.

Promise.all fait seulement wait plusieurs promesses. Peu importe l'ordre dans lequel ils sont résolus ou si les calculs sont exécutés en parallèle.

existe-t-il un moyen pratique d'exécuter un itératif séquentiellement?

Si vous avez déjà vos promesses, vous ne pouvez pas faire grand chose d'autre que Promise.all([p1, p2, p3, …]) (qui n'a pas de notion de séquence). Mais si vous avez un nombre itérable de fonctions asynchrones, vous pouvez en effet les exécuter de manière séquentielle. Fondamentalement, vous devez obtenir de 

[fn1, fn2, fn3, …]

à

fn1().then(fn2).then(fn3).then(…)

et la solution consiste à utiliser Array::reduce :

iterable.reduce((p, fn) => p.then(fn), Promise.resolve())
182
Bergi

En parallèle

await Promise.all(items.map(async item => { await fetchItem(item) }))

Avantages: plus rapide. Toutes les itérations seront exécutées même en cas d'échec.

En séquence

for (let i = 0; i < items.length; i++) {
    await fetchItem(items[i])
}

Avantages: les variables de la boucle peuvent être partagées à chaque itération. Se comporte comme un code synchrone impératif normal.

30
david_adler

La réponse de Bergis m'a mis sur la bonne voie en utilisant Array.reduce.

Cependant, pour que les fonctions renvoyant mes promesses s’exécutent les unes après les autres, je devais ajouter plus de fonctions d’imbrication.

Mon cas d'utilisation réel est un tableau de fichiers que je dois transférer l'un après l'autre en raison de limites en aval ...

Voici ce que j'ai fini avec.

getAllFiles().then( (files) => {
    return files.reduce((p, theFile) => {
        return p.then(() => {
            return transferFile(theFile); //function returns a promise
        });
    }, Promise.resolve()).then(()=>{
        console.log("All files transferred");
    });
}).catch((error)=>{
    console.log(error);
});

Comme le suggèrent les réponses précédentes, en utilisant:

getAllFiles().then( (files) => {
    return files.reduce((p, theFile) => {
        return p.then(transferFile(theFile));
    }, Promise.resolve()).then(()=>{
        console.log("All files transferred");
    });
}).catch((error)=>{
    console.log(error);
});

n’a pas attendu la fin du transfert pour en lancer un autre et le texte "Tous les fichiers transférés" est arrivé avant même que le premier transfert de fichier ait été lancé.

Pas sûr de ce que j'ai fait de mal, mais je voulais partager ce qui a fonctionné pour moi.

Edit: Depuis que j'ai écrit ce post, je comprends maintenant pourquoi la première version n'a pas fonctionné. then () attend une fonctionretournant une promesse. Donc, vous devriez passer le nom de la fonction sans parenthèses! Maintenant, ma fonction veut un argument, je dois donc envelopper une fonction anonyme sans argument!

9
tkarls

Vous pouvez également traiter séquentiellement une fonction itérative avec une fonction asynchrone à l'aide d'une fonction récursive. Par exemple, avec un tableau a à traiter avec la fonction asynchrone someAsyncFunction()

var a = [1, 2, 3, 4, 5, 6]

function someAsyncFunction(n) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("someAsyncFunction: ", n)
      resolve(n)
    }, Math.random() * 1500)
  })
}

//You can run each array sequentially with: 

function sequential(arr, index = 0) {
  if (index >= arr.length) return Promise.resolve()
  return someAsyncFunction(arr[index])
    .then(r => {
      console.log("got value: ", r)
      return sequential(arr, index + 1)
    })
}

sequential(a).then(() => console.log("done"))

3
Mark Meyer

juste pour élaborer sur la réponse de @ Bergi (qui est très succincte, mais difficile à comprendre;)

Ce code exécutera chaque élément du tableau et ajoutera le prochain "puis chaîne" à la fin;

function eachorder(prev,order) {
        return prev.then(function() {
          return get_order(order)
            .then(check_order)
            .then(update_order);
        });
    }
orderArray.reduce(eachorder,Promise.resolve());

espérons que cela a du sens.

3
TimoSolo

cela pourrait répondre à une partie de votre question.

oui, vous pouvez chaîner un tableau de fonctions renvoyant des promesses comme suit:. vous pouvez bien sûr l'éditer pour passer le même argument (ou aucun argument) à chaque fonction.

function tester1(a) {
  return new Promise(function(done) {
    setTimeout(function() {
      done(a + 1);
    }, 1000);
  })
}

function tester2(a) {
  return new Promise(function(done) {
    setTimeout(function() {
      done(a * 5);
    }, 1000);
  })
}

function promise_chain(args, list, results) {

  return new Promise(function(done, errs) {
    var fn = list.shift();
    if (results === undefined) results = [];
    if (typeof fn === 'function') {
      fn(args).then(function(result) {
        results.Push(result);
        console.log(result);
        promise_chain(result, list, results).then(done);
      }, errs);
    } else {
      done(results);
    }

  });

}

promise_chain(0, [tester1, tester2, tester1, tester2, tester2]).then(console.log.bind(console), console.error.bind(console));

0
cestmoi

Je me sers de pour résoudre des promesses séquentielles. Je ne suis pas sûr que cela aide, mais c'est ce que je faisais.

async function run() {
    for (let val of arr) {
        const res = await someQuery(val)
        console.log(val)
    }
}

run().then().catch()
0
Nick Kotenberg

En utilisant async wait, un tableau de promesses peut facilement être exécuté séquentiellement:

let a = [promise1, promise2, promise3];

async function func() {
  for(let i=0; i<a.length; i++){
    await a[i]();
  }  
}

func();

Remarque: dans l'implémentation ci-dessus, si une promesse est rejetée, le reste ne sera pas exécuté. Si vous souhaitez que toutes vos promesses soient exécutées, placez votre await a[i](); à l'intérieur de try catch

0
Ayan

Vous pouvez le faire par boucle.

retour de fonction asynchrone

async function createClient(client) {
    return await Client.create(client);
}

let clients = [client1, client2, client3];

si vous écrivez le code suivant, les clients sont créés parallèlement

const createdClientsArray = yield Promise.all(clients.map((client) =>
    createClient(client);
));

alors tous les clients sont créés en parallèle. mais si vous voulez créer le client séquentiellement, vous devez utiliser la boucle

const createdClientsArray = [];
for(let i = 0; i < clients.length; i++) {
    const createdClient = yield createClient(clients[i]);
    createdClientsArray.Push(createdClient);
}

alors tous les clients sont créés séquentiellement.

bonne codage :)

0
Deepak Sisodiya

La réponse de Bergi m'a aidé à rendre l'appel synchrone. J'ai ajouté un exemple ci-dessous dans lequel nous appelons chaque fonction après l'appel de la fonction précédente.

function func1 (param1) {
    console.log("function1 : " + param1);
}
function func2 () {
    console.log("function2");
}
function func3 (param2, param3) {
    console.log("function3 : " + param2 + ", " + param3);
}

function func4 (param4) {
    console.log("function4 : " + param4);
}
param4 = "Kate";

//adding 3 functions to array

a=[
    ()=>func1("Hi"),
    ()=>func2(),
    ()=>func3("Lindsay",param4)
  ];

//adding 4th function

a.Push(()=>func4("dad"));

//below does func1().then(func2).then(func3).then(func4)

a.reduce((p, fn) => p.then(fn), Promise.resolve());
0
Nithi