Gibt es einen Unterschied zwischen:
const [result1, result2] = await Promise.all([task1(), task2()]);
und
const t1 = task1();
const t2 = task2();
const result1 = await t1;
const result2 = await t2;
und
const [t1, t2] = [task1(), task2()];
const [result1, result2] = [await t1, await t2];
javascript
async-await
Versteckt
quelle
quelle
Antworten:
Für die Zwecke dieser Antwort werde ich einige Beispielmethoden verwenden:
res(ms)
ist eine Funktion, die eine Ganzzahl von Millisekunden benötigt und ein Versprechen zurückgibt, das nach so vielen Millisekunden aufgelöst wird.rej(ms)
ist eine Funktion, die eine Ganzzahl von Millisekunden benötigt und ein Versprechen zurückgibt, das nach so vielen Millisekunden abgelehnt wird.Der Anruf
Beispiel 1res
startet den Timer. Wenn SiePromise.all
auf eine Handvoll Verzögerungen warten, wird das Problem behoben, nachdem alle Verzögerungen abgeschlossen sind. Denken Sie jedoch daran, dass sie gleichzeitig ausgeführt werden:Code-Snippet anzeigen
Dies bedeutet, dass
Promise.all
mit den Daten aus den inneren Versprechungen nach 3 Sekunden aufgelöst wird.Hat
Beispiel 2Promise.all
aber ein "schnelles Versagen" -Verhalten :Code-Snippet anzeigen
Wenn Sie
Beispiel 3async-await
stattdessen verwenden, müssen Sie warten, bis jedes Versprechen nacheinander aufgelöst wird. Dies ist möglicherweise nicht so effizient:Code-Snippet anzeigen
quelle
unhandledrejection
Fehler verursachen . Sie werden dies niemals verwenden wollen. Bitte fügen Sie dies Ihrer Antwort hinzu.Erster Unterschied - schnell scheitern
Ich stimme der Antwort von @ zzzzBov zu, aber der Vorteil von Promise.all ist nicht nur der einzige Unterschied. Einige Benutzer in Kommentaren fragen, warum Promise.all verwendet werden soll, wenn es nur im negativen Szenario schneller ist (wenn eine Aufgabe fehlschlägt). Und ich frage warum nicht? Wenn ich zwei unabhängige asynchrone parallele Aufgaben habe und die erste in sehr langer Zeit gelöst wird, die zweite jedoch in sehr kurzer Zeit abgelehnt wird, warum sollte der Benutzer auf die Fehlermeldung "sehr lange Zeit" anstatt "sehr kurze Zeit" warten? In realen Anwendungen müssen wir ein negatives Szenario berücksichtigen. Aber OK - in diesem ersten Unterschied können Sie entscheiden, welche Alternative Promise.all oder mehrere verwenden sollen.
Zweiter Unterschied - Fehlerbehandlung
Wenn Sie jedoch über eine Fehlerbehandlung nachdenken, MÜSSEN SIE Promise.all verwenden. Es ist nicht möglich, Fehler von asynchronen parallelen Aufgaben, die mit mehreren Wartezeiten ausgelöst wurden, korrekt zu behandeln. Im negativen Szenario enden Sie immer mit
UnhandledPromiseRejectionWarning
undPromiseRejectionHandledWarning
obwohl Sie try / catch überall verwenden. Deshalb wurde Promise.all entwickelt. Natürlich könnte jemand sagen, dass wir diese Fehler mitprocess.on('unhandledRejection', err => {})
und unterdrücken könnenprocess.on('rejectionHandled', err => {})
aber es ist keine gute Praxis. Ich habe im Internet viele Beispiele gefunden, bei denen die Fehlerbehandlung für zwei oder mehr unabhängige asynchrone parallele Aufgaben überhaupt nicht oder nur in falscher Weise berücksichtigt wird - nur mit try / catch und in der Hoffnung, dass Fehler abgefangen werden. Es ist fast unmöglich, eine gute Praxis zu finden. Deshalb schreibe ich diese Antwort.Zusammenfassung
Verwenden Sie niemals mehrere Wartezeiten für zwei oder mehr unabhängige asynchrone parallele Aufgaben, da Sie Fehler nicht ernsthaft behandeln können. Verwenden Sie für diesen Anwendungsfall immer Promise.all (). Async / await ist kein Ersatz für Promises. Es ist nur eine hübsche Art, Versprechen zu verwenden ... asynchroner Code ist im Synchronisierungsstil geschrieben und wir können mehrere vermeiden
then
Versprechen .Einige Leute sagen, dass wir mit Promise.all () Aufgabenfehler nicht separat behandeln können, sondern nur Fehler aus dem ersten abgelehnten Versprechen (ja, einige Anwendungsfälle erfordern möglicherweise eine separate Behandlung, z. B. für die Protokollierung). Es ist kein Problem - siehe "Addition" unten.
Beispiele
Betrachten Sie diese asynchrone Aufgabe ...
Wenn Sie Aufgaben in einem positiven Szenario ausführen, gibt es keinen Unterschied zwischen Promise.all und Multiple Wait. Beide Beispiele enden
Task 1 succeed! Task 2 succeed!
nach 5 Sekunden.Wenn die erste Aufgabe im positiven Szenario 10 Sekunden und die zweite Aufgabe im negativen Szenario 5 Sekunden dauert, gibt es Unterschiede bei den ausgegebenen Fehlern.
Wir sollten hier bereits bemerken, dass wir etwas falsch machen, wenn wir mehrere Wartezeiten parallel verwenden. Um Fehler zu vermeiden, sollten wir natürlich damit umgehen! Lass es uns versuchen...
Wie Sie sehen können, müssen wir nur einen Fang zur
run
Funktion hinzufügen, und der Code mit der Fanglogik befindet sich im Rückruf ( asynchroner Stil ), um Fehler erfolgreich zu behandeln . Wir brauchen keine Fehler innerhalb derrun
Funktion zu behandeln, da die asynchrone Funktion automatisch ausgeführt wird. Das Versprechen der Zurückweisung dertask
Funktion führt zur Zurückweisung derrun
Funktion. Um Rückrufe zu vermeiden, können wir den Synchronisierungsstil (async / await + try / catch) verwenden.try { await run(); } catch(err) { }
In diesem Beispiel ist dies jedoch nicht möglich, da wir ihn nichtawait
im Hauptthread verwenden können. Er kann nur in der asynchronen Funktion verwendet werden (logisch, weil niemand dies möchte Hauptgewinde blockieren). So testen Sie, ob die Behandlung im Synchronisierungsstil , können wir aufrufenrun
von einer anderen asynchronen Funktion aus funktioniert, oder verwenden Sie IIFE (Sofort aufgerufener Funktionsausdruck) :(async function() { try { await run(); } catch(err) { console.log('Caught error', err); }; })();
.Dies ist nur eine korrekte Methode, um zwei oder mehr asynchrone parallele Aufgaben auszuführen und Fehler zu behandeln. Sie sollten Beispiele unten vermeiden.
Wir können versuchen, Code auf verschiedene Arten zu verarbeiten ...
... nichts wurde abgefangen, weil es Synchronisationscode verarbeitet, aber
run
asynchron ist... Wtf? Wir sehen erstens, dass der Fehler für Aufgabe 2 nicht behandelt wurde und später abgefangen wurde. Irreführend und immer noch voller Fehler in der Konsole. Auf diese Weise nicht verwendbar.
... das gleiche wie oben. Benutzer @Qwerty fragte in seiner gelöschten Antwort nach diesem seltsamen Verhalten, das anscheinend abgefangen wird, aber es gibt auch nicht behandelte Fehler. Wir fangen einen Fehler ab, weil run () online mit dem Schlüsselwort await abgelehnt wird und beim Aufruf von run () mit try / catch abgefangen werden kann. Wir erhalten auch einen nicht behandelten Fehler, weil wir die asynchrone Taskfunktion synchron aufrufen (ohne das Schlüsselwort wait) und diese Task außerhalb der Funktion run () ausgeführt wird und auch außerhalb fehlschlägt. Es ist ähnlich, wenn wir beim Aufrufen einer Synchronisierungsfunktion, deren Codeteil in setTimeout ausgeführt wird, nicht in der Lage sind, Fehler durch try / catch zu behandeln
function test() { setTimeout(function() { console.log(causesError); }, 0); }; try { test(); } catch(e) { /* this will never catch error */ }
.... "nur" zwei Fehler (dritter fehlt) aber nichts abgefangen.
Ergänzung (Behandeln Sie Aufgabenfehler separat und auch First-Fail-Fehler)
... beachten Sie, dass ich in diesem Beispiel für beide Aufgaben negativeScenario = true verwendet habe, um besser zu demonstrieren, was passiert (
throw err
wird verwendet, um den endgültigen Fehler auszulösen).quelle
Sie können es selbst überprüfen.
In dieser Geige habe ich einen Test durchgeführt, um die Blockierungsnatur von zu demonstrieren
await
, im Gegensatz dazuPromise.all
werden alle Versprechen gestartet und während einer wartet, wird es mit den anderen weitergehen.quelle
t1 = task1(); t2 = task2()
und dem anschließenden Verwendenawait
für beideresult1 = await t1; result2 = await t2;
wie in seiner Frage, im Gegensatz zu dem, was Sie testen, dasawait
beim ursprünglichen Anruf verwendet wirdresult1 = await task1(); result2 = await task2();
. Der Code in seiner Frage startet alle Versprechen auf einmal. Der Unterschied besteht, wie die Antwort zeigt, darin, dass Fehler auf demPromise.all
Weg schneller gemeldet werden.Bei Verwendung von
Promise.all()
Laufanforderungen wird im Allgemeinen "asynchron" parallel ausgeführt. Die Verwendungawait
kann parallel ausgeführt werden ODER eine "Synchronisierungs" -Blockierung sein.Die folgenden Funktionen test1 und test2 zeigen, wie
await
asynchron oder synchron ausgeführt werden kann.Test3 zeigt,
Promise.all()
dass dies asynchron ist.jsfiddle with timed results - Öffnen Sie die Browserkonsole, um die Testergebnisse anzuzeigen
Synchronisierungsverhalten . Läuft NICHT parallel, dauert ~ 1800ms :
Asynchrones Verhalten. Läuft parallel, dauert ~ 600ms :
Asynchrones Verhalten. Läuft parallel, dauert ~ 600ms :
TLDR; Wenn Sie es verwenden
Promise.all()
, wird es auch "schnell fehlschlagen" - stoppen Sie die Ausführung zum Zeitpunkt des ersten Ausfalls einer der enthaltenen Funktionen.quelle
Im Falle von Warten auf Promise.all ([task1 (), task2 ()]); "task1 ()" und "task2 ()" werden parallel ausgeführt und warten, bis beide Versprechen erfüllt sind (entweder gelöst oder abgelehnt). Im Falle von
t2 wird erst ausgeführt, nachdem t1 die Ausführung beendet hat (wurde aufgelöst oder abgelehnt). Sowohl t1 als auch t2 laufen nicht parallel.
quelle