Je voudrais clarifier ce point, car la documentation n'est pas trop claire à ce sujet;
Q1: Le Promise.all(iterable)
traitement de toutes les promesses est-il séquentiel ou parallèle? Ou, plus précisément, est-ce l'équivalent d'exécuter des promesses enchaînées comme
p1.then(p2).then(p3).then(p4).then(p5)....
ou est - il un autre type d'algorithme où tout 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 resolve (ou on rejette)?
Q2: Si Promise.all
s'exécute en parallèle, existe-t-il un moyen pratique d'exécuter un itérable séquentiellement?
Remarque : je ne veux pas utiliser Q ou Bluebird, mais toutes les spécifications ES6 natives.
javascript
node.js
promise
es6-promise
Yanick Rochon
la source
la source
Promise.all
exécuter en parallèle.node.js
etio.js
comme c'est là que je l'utilise. Donc, oui, l'implémentation V8 si vous voulez.Promise.all
.new Promise(a).then(b); c();
a est exécuté d'abord, puis c, puis b. Ce n'est pas Promise.all qui exécute ces promesses, il gère juste quand elles se résolvent.Réponses:
Non, les promesses ne peuvent pas «être exécutées». Ils commencent leur tâche lors de leur création - ils ne représentent que les résultats - et vous exécutez tout en parallèle avant même de les transmettre
Promise.all
.Promise.all
ne fait que Attendre les multiples promesses. Peu importe dans quel ordre ils résolvent ou si les calculs sont exécutés en parallèle.Si vous avez déjà vos promesses, vous ne pouvez pas faire grand chose mais
Promise.all([p1, p2, p3, …])
(qui n'a pas de notion de séquence). Mais si vous avez un itérable de fonctions asynchrones, vous pouvez en effet les exécuter séquentiellement. Fondamentalement, vous devez obtenir deà
et la solution pour ce faire est d'utiliser
Array::reduce
:la source
then
séquence - la valeur de retour est la promesse du dernierfn
résultat, et vous pouvez enchaîner d'autres rappels à cela.fn1().then(p2).then(fn3).catch(…
? Pas besoin d'utiliser une expression de fonction.retValFromF1
est transmisp2
, c'est exactement ce quep2
fait. Bien sûr, si vous voulez faire plus (passer des variables supplémentaires, appeler plusieurs fonctions, etc.), vous devez utiliser une expression de fonction, bien que changerp2
dans le tableau serait plus facileiterable
c'est le[fn1, fn2, fn3, …]
tableauEn parallèle
Avantages: plus rapide. Toutes les itérations seront exécutées même en cas d'échec.
En séquence
Avantages: les variables de la boucle peuvent être partagées à chaque itération. Se comporte comme un code synchrone impératif normal.
la source
for (const item of items) await fetchItem(item);
await Promise.all(items.map(async item => { return await fetchItem(item).catch(e => e) }))
async
fonction est un appel d'API et que vous ne voulez pas DDOS le serveur. Vous avez un meilleur contrôle sur les résultats individuels et les erreurs générées lors de l'exécution. Mieux encore, vous pouvez décider des erreurs à continuer et de ce qu'il faut briser la boucle.La réponse de Bergis m'a mis sur la bonne voie en utilisant Array.reduce.
Cependant, pour que les fonctions renvoient mes promesses de s'exécuter l'une après l'autre, j'ai dû ajouter un peu plus d'imbrication.
Mon cas d'utilisation réel est un tableau de fichiers que je dois transférer les uns après les autres en raison des limites en aval ...
Voici ce avec quoi j'ai fini.
Comme le suggèrent les réponses précédentes, en utilisant:
n'a pas attendu la fin du transfert avant d'en démarrer un autre et le texte "Tous les fichiers transférés" est venu avant même le début du premier transfert de fichiers.
Je ne sais pas 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 fonction renvoyant une promesse. Donc, vous devez passer le nom de la fonction sans parenthèses! Maintenant, ma fonction veut un argument alors je dois encapsuler dans une fonction anonyme ne prenant aucun argument!
la source
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 'then chain' à la fin;
espérons que cela a du sens.
la source
Vous pouvez également traiter un itératif de manière séquentielle avec une fonction asynchrone à l'aide d'une fonction récursive. Par exemple, étant donné un tableau
a
à traiter avec une fonction asynchronesomeAsyncFunction()
:la source
array.prototype.reduce
est bien meilleure en termes de performances qu'une fonction récursivereduce
où vous devez construire lathen()
chaîne entière en une seule étape, puis l'exécuter.NodeJS n'exécute pas les promesses en parallèle, il les exécute simultanément car il s'agit d'une architecture de boucle d'événements à thread unique. Il est possible d'exécuter les choses en parallèle en créant un nouveau processus enfant pour tirer parti du processeur multicœur.
Parallèle Vs Concurent
En fait, ce
Promise.all
que fait, c'est empiler les promesses dans la file d'attente appropriée (voir l'architecture de la boucle d'événements) les exécuter simultanément (appeler P1, P2, ...) puis attendre chaque résultat, puis résoudre le Promise.all avec toutes les promesses résultats. Promise.all échouera à la première promesse qui échouera, à moins que vous n'ayez géré vous-même le rejet.Il y a une différence majeure entre parallèle et concurrent, le premier exécutera un calcul différent dans un processus séparé exactement au même moment et ils progresseront à ce rythme, tandis que l'autre exécutera les différents calculs l'un après l'autre sans attendre le précédent. calcul pour finir et progresser en même temps sans dépendre les uns des autres.
Enfin, pour répondre à votre question,
Promise.all
ne s'exécutera ni en parallèle ni séquentiellement mais simultanément.la source
En utilisant async await, un tableau de promesses peut facilement être exécuté séquentiellement:
Remarque: dans l'implémentation ci-dessus, si une promesse est rejetée, le reste ne sera pas exécuté.Si vous voulez que toutes vos promesses soient exécutées, enveloppez votre
await a[i]();
intérieurtry catch
la source
parallèle
voir cet exemple
en exécutant le code, il console "APPELÉ" pour les six promesses et quand elles sont résolues, il console toutes les 6 réponses après expiration du délai en même temps
la source
La réponse de Bergi m'a aidé à rendre l'appel synchrone.J'ai ajouté un exemple ci-dessous où nous appelons chaque fonction après l'appel de la fonction précédente.
la source
Vous pouvez le faire par boucle for.
promesse de retour de fonction asynchrone
si vous écrivez le code suivant, le client est créé en parallèle
alors tous les clients sont créés en parallèle. mais si vous souhaitez créer un client séquentiellement, vous devez utiliser la boucle for
puis tous les clients sont créés séquentiellement.
bon codage :)
la source
async
/await
n'est disponible qu'avec un transpilateur ou en utilisant d' autres moteurs que Node. En outre, vous ne devriez vraiment pas mélangerasync
avecyield
. Bien qu'ils agissent de la même manière avec un transpilateur etco
, ils sont vraiment très différents et ne devraient normalement pas se substituer l'un à l'autre. Vous devez également mentionner ces restrictions car votre réponse est déroutante pour les programmeurs novices.J'utilise for of afin de résoudre des promesses séquentielles. Je ne sais pas si cela aide ici, mais c'est ce que j'ai fait.
la source
cela pourrait répondre à une partie de votre question.
oui, vous pouvez enchaîner un tableau de fonctions de retour de promesse comme suit ... (cela passe le résultat de chaque fonction à la suivante). vous pouvez bien sûr le modifier pour passer le même argument (ou aucun argument) à chaque fonction.
la source
Je suis tombé sur cette page en essayant de résoudre un problème dans NodeJS: réassemblage de morceaux de fichiers. Fondamentalement: j'ai un tableau de noms de fichiers. Je dois ajouter tous ces fichiers, dans le bon ordre, pour créer un seul gros fichier. Je dois le faire de manière asynchrone.
Le module 'fs' de Node fournit appendFileSync mais je ne voulais pas bloquer le serveur pendant cette opération. Je voulais utiliser le module fs.promises et trouver un moyen d'enchaîner ces choses. Les exemples de cette page ne fonctionnaient pas tout à fait pour moi car j'avais en fait besoin de deux opérations: fsPromises.read () pour lire le bloc de fichier et fsPromises.appendFile () pour concaténer le fichier de destination. Peut-être que si j'étais meilleur avec javascript, j'aurais pu faire fonctionner les réponses précédentes pour moi. ;-)
Je suis tombé sur ceci ... https://css-tricks.com/why-using-reduce-to-sequentially-resolve-promises-works/ ... et j'ai pu pirater ensemble une solution de travail.
TLDR:
Et voici un test unitaire de jasmin pour cela:
J'espère que cela aide quelqu'un.
la source