Y a-t-il une différence entre:
const [result1, result2] = await Promise.all([task1(), task2()]);
et
const t1 = task1();
const t2 = task2();
const result1 = await t1;
const result2 = await t2;
et
const [t1, t2] = [task1(), task2()];
const [result1, result2] = [await t1, await t2];
javascript
async-await
Caché
la source
la source
Réponses:
Pour les besoins de cette réponse, j'utiliserai quelques exemples de méthodes:
res(ms)
est une fonction qui prend un entier de millisecondes et renvoie une promesse qui se résout au bout de plusieurs millisecondes.rej(ms)
est une fonction qui prend un entier de millisecondes et renvoie une promesse qui rejette après ce nombre de millisecondes.L'appel
Exemple 1res
lance le chronomètre. UtiliserPromise.all
pour attendre une poignée de retards résoudra une fois tous les retards terminés, mais rappelez-vous qu'ils s'exécutent en même temps:Afficher l'extrait de code
Cela signifie que
Promise.all
cela résoudra avec les données des promesses internes après 3 secondes.Mais,
Exemple # 2Promise.all
a un comportement «échec rapide» :Afficher l'extrait de code
Si vous utilisez à la
Exemple # 3async-await
place, vous devrez attendre que chaque promesse se résout séquentiellement, ce qui peut ne pas être aussi efficace:Afficher l'extrait de code
la source
unhandledrejection
erreurs. Vous ne voudrez jamais l'utiliser. Veuillez ajouter ceci à votre réponse.Première différence - échouer rapidement
Je suis d'accord avec la réponse de @ zzzzBov mais l'avantage "échouer rapidement" de Promise.all n'est pas seulement la seule différence. Certains utilisateurs dans les commentaires demandent pourquoi utiliser Promise.all alors qu'il n'est plus rapide que dans un scénario négatif (lorsqu'une tâche échoue). Et je demande pourquoi pas? Si j'ai deux tâches parallèles asynchrones indépendantes et que la première est résolue dans un temps très long, mais la seconde est rejetée dans un délai très court, pourquoi laisser l'utilisateur attendre le message d'erreur "très long temps" au lieu de "très court temps"? Dans les applications réelles, nous devons envisager un scénario négatif. Mais OK - dans cette première différence, vous pouvez décider quelle alternative utiliser Promise.all par rapport à plusieurs attendent.
Deuxième différence - gestion des erreurs
Mais lorsque vous envisagez de gérer les erreurs, VOUS DEVEZ utiliser Promise.all. Il n'est pas possible de gérer correctement les erreurs des tâches parallèles asynchrones déclenchées avec plusieurs wait. Dans un scénario négatif, vous finirez toujours par
UnhandledPromiseRejectionWarning
etPromiseRejectionHandledWarning
bien que vous utilisiez try / catch n'importe où. C'est pourquoi Promise.all a été conçu. Bien sûr, quelqu'un pourrait dire que nous pouvons supprimer les erreurs en utilisantprocess.on('unhandledRejection', err => {})
etprocess.on('rejectionHandled', err => {})
mais ce n'est pas une bonne pratique. J'ai trouvé de nombreux exemples sur Internet qui ne prennent pas du tout en compte la gestion des erreurs pour deux ou plusieurs tâches parallèles asynchrones indépendantes ou qui ne le considèrent pas mais de la mauvaise manière - en utilisant simplement try / catch et en espérant qu'il détectera les erreurs. Il est presque impossible de trouver de bonnes pratiques. C'est pourquoi j'écris cette réponse.Résumé
N'utilisez jamais d'attente multiple pour deux ou plusieurs tâches parallèles asynchrones indépendantes car vous ne serez pas en mesure de gérer les erreurs sérieusement. Utilisez toujours Promise.all () pour ce cas d'utilisation. Async / await ne remplace pas les promesses. C'est juste une jolie manière d'utiliser les promesses ... le code async est écrit dans le style de synchronisation et nous pouvons éviter plusieurs
then
promesses.Certaines personnes disent qu'en utilisant Promise.all (), nous ne pouvons pas gérer les erreurs de tâches séparément mais seulement l'erreur de la première promesse rejetée (oui, certains cas d'utilisation peuvent nécessiter une gestion séparée, par exemple pour la journalisation). Ce n'est pas un problème - voir l'en-tête "Ajout" ci-dessous.
Exemples
Considérez cette tâche asynchrone ...
Lorsque vous exécutez des tâches dans un scénario positif, il n'y a aucune différence entre Promise.all et multiple await. Les deux exemples se terminent
Task 1 succeed! Task 2 succeed!
après 5 secondes.Lorsque la première tâche prend 10 secondes dans un scénario positif et que la tâche secondes prend 5 secondes dans un scénario négatif, il existe des différences dans les erreurs émises.
Nous devrions déjà remarquer ici que nous faisons quelque chose de mal lors de l'utilisation de plusieurs wait en parallèle. Bien sûr, pour éviter les erreurs, nous devons le gérer! Essayons...
Comme vous pouvez le voir pour gérer correctement l'erreur, nous devons ajouter une seule capture à la
run
fonction et le code avec la logique de capture est en rappel ( style asynchrone ). Nous n'avons pas besoin de gérer les erreurs à l'intérieur de larun
fonction car la fonction asynchrone le fait automatiquement - la promesse de rejet de latask
fonction entraîne le rejet de larun
fonction. Pour éviter le rappel, nous pouvons utiliser le style de synchronisation (async / await + try / catch)try { await run(); } catch(err) { }
mais dans cet exemple, ce n'est pas possible car nous ne pouvons pas l'utiliserawait
dans le thread principal - il ne peut être utilisé que dans la fonction async (c'est logique car personne ne veut bloquer le thread principal). Pour tester si la gestion fonctionne dans le style de synchronisation, nous pouvons appelerrun
fonction d' une autre fonction asynchrone ou utilisation Ia vie (Expression immédiatement Appelé Function):(async function() { try { await run(); } catch(err) { console.log('Caught error', err); }; })();
.Ce n'est qu'une façon correcte d'exécuter deux ou plusieurs tâches parallèles asynchrones et de gérer les erreurs. Vous devriez éviter les exemples ci-dessous.
Nous pouvons essayer de gérer le code de plusieurs manières ...
... rien n'a été attrapé car il gère le code de synchronisation mais
run
est asynchrone... Wtf? Nous voyons tout d'abord que l'erreur de la tâche 2 n'a pas été gérée et plus tard qu'elle a été interceptée. Trompeur et toujours plein d'erreurs dans la console. Inutilisable de cette façon.
... le même que ci-dessus. L'utilisateur @Qwerty, dans sa réponse supprimée, a posé des questions sur ce comportement étrange qui semble être attrapé, mais il y a aussi des erreurs non gérées. Nous détectons l'erreur car run () est rejeté en ligne avec le mot-clé await et peut être détecté en utilisant try / catch lors de l'appel de run (). Nous obtenons également une erreur non gérée car nous appelons la fonction de tâche asynchrone de manière synchrone (sans mot-clé await) et cette tâche s'exécute en dehors de la fonction run () et échoue également à l'extérieur. Il est similaire quand nous ne sommes pas en mesure de gérer l' erreur par try / catch lorsque vous appelez une fonction de synchronisation qui partie de code se exécute dans setTimeout ...
function test() { setTimeout(function() { console.log(causesError); }, 0); }; try { test(); } catch(e) { /* this will never catch error */ }
.... "seulement" deux erreurs (la troisième est manquante) mais rien n'a été détecté.
Ajout (gérer les erreurs de tâche séparément et également l'erreur de premier échec)
... notez que dans cet exemple, j'ai utilisé negativeScenario = true pour les deux tâches pour une meilleure démonstration de ce qui se passe (
throw err
est utilisé pour déclencher l'erreur finale)la source
Généralement, l'utilisation
Promise.all()
exécute des requêtes "asynchrones" en parallèle. L'utilisationawait
peut s'exécuter en parallèle OU être un blocage de «synchronisation».Les fonctions test1 et test2 ci-dessous montrent comment
await
exécuter async ou sync.test3 montre
Promise.all()
que c'est asynchrone.jsfiddle avec des résultats chronométrés - ouvrez la console du navigateur pour voir les résultats des tests
Comportement de synchronisation . Ne fonctionne PAS en parallèle, prend ~ 1800 ms :
Comportement asynchrone . Fonctionne en parallèle, prend ~ 600 ms :
Comportement asynchrone . Fonctionne en parallèle, prend ~ 600 ms :
TLDR; Si vous l'utilisez,
Promise.all()
il "échouera rapidement" - arrêtez de fonctionner au moment du premier échec de l'une des fonctions incluses.la source
Vous pouvez vérifier par vous-même.
Dans ce violon , j'ai fait un test pour démontrer la nature bloquante de
await
, par opposition àPromise.all
laquelle déclenchera toutes les promesses et pendant que l'une attend, elle continuera avec les autres.la source
t1 = task1(); t2 = task2()
et puis en utilisantawait
ensuite pour les deuxresult1 = await t1; result2 = await t2;
comme dans sa question, par opposition à ce que vous testez qui utiliseawait
l'appel original commeresult1 = await task1(); result2 = await task2();
. Le code dans sa question lance toutes les promesses à la fois. La différence, comme le montre la réponse, est que les échecs seront signalés plus rapidement avec lePromise.all
chemin.En cas d' attente de Promise.all ([task1 (), task2 ()]); "task1 ()" et "task2 ()" fonctionneront en parallèle et attendront que les deux promesses soient complétées (résolues ou rejetées). Alors qu'en cas de
t2 ne fonctionnera qu'après la fin de l'exécution de t1 (résolu ou rejeté). Les deux t1 et t2 ne fonctionneront pas en parallèle.
la source