Nehmen wir an, ich habe eine Reihe von Promise
s, die Netzwerkanforderungen stellen, von denen eine fehlschlagen wird:
// http://does-not-exist will throw a TypeError
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]
Promise.all(arr)
.then(res => console.log('success', res))
.catch(err => console.log('error', err)) // This is executed
Nehmen wir an, ich möchte warten, bis alle abgeschlossen sind, unabhängig davon, ob einer fehlgeschlagen ist. Möglicherweise liegt ein Netzwerkfehler für eine Ressource vor, ohne die ich leben kann. Wenn ich sie jedoch erhalten kann, möchte ich, bevor ich fortfahre. Ich möchte Netzwerkfehler ordnungsgemäß behandeln.
Da Promises.all
keinen Raum dafür lassen, was ist die empfohlene Muster diese für die Handhabung, ohne Versprechen Bibliothek?
javascript
promise
es6-promise
Nathan Hagen
quelle
quelle
allSettled
Ihren Anforderungen entspricht, ohne dass Sie Ihre eigenen rollen müssen.Promise.all
wird abgelehnt, sobald ein Versprechen abgelehnt wird , sodass Ihre vorgeschlagene Redewendung nicht garantiert, dass alle Versprechen erfüllt werden.Antworten:
Update, Sie möchten wahrscheinlich das integrierte native verwenden
Promise.allSettled
:Als lustige Tatsache war diese Antwort unten Stand der Technik beim Hinzufügen dieser Methode zur Sprache:]
Klar, du brauchst nur ein
reflect
:Oder mit ES5:
Oder in Ihrem Beispiel:
quelle
reflect
ein allgemeines Wort in der Informatik? Kannst du bitte verlinken, wo dies wie auf Wikipedia erklärt wird oder so. Ich suchte intensivPromise.all not even first reject
, wusste aber nicht, ob ich nach "Reflect" suchen sollte. Sollte ES6 eine haben,Promise.reflect
die wie "Promise.all aber wirklich alle" ist?Ähnliche Antwort, aber vielleicht idiomatischer für ES6:
Je nach Art (en) der Wert zurückgegeben, können Fehler oft leicht genug unterschieden werden (zB Verwendung
undefined
für „do not care“,typeof
für Normal Nicht-Objektwert,result.message
,result.toString().startsWith("Error:")
etc.)quelle
.map(p => p.catch(e => e))
Teil alle Ablehnungen in aufgelöste WertePromise.all
umwandelt. Warten Sie also immer noch , bis alles abgeschlossen ist, ob einzelne Funktionen aufgelöst oder abgelehnt werden, unabhängig davon, wie lange sie dauern. Versuch es..catch(e => console.log(e));
wird nie genannt, weil dies nie fehlschlägtcatch
Allgemeinen eine gute Praxis ist .e
und gibt ihn als regulären (Erfolgs-) Wert zurück. Gleich wiep.catch(function(e) { return e; })
nur kürzer.return
ist implizit.Benjamins Antwort bietet eine großartige Abstraktion zur Lösung dieses Problems, aber ich hatte auf eine weniger abstrahierte Lösung gehofft. Die explizite Möglichkeit, dieses Problem zu beheben, besteht darin, einfach
.catch
die internen Versprechen aufzurufen und den Fehler von ihrem Rückruf zurückzugeben.Wenn Sie noch einen Schritt weiter gehen, können Sie einen generischen Catch-Handler schreiben, der folgendermaßen aussieht:
dann kannst du tun
Das Problem dabei ist, dass die abgefangenen Werte eine andere Schnittstelle haben als die nicht abgefangenen Werte. Um dies zu bereinigen, können Sie Folgendes tun:
Jetzt können Sie Folgendes tun:
Dann, um es trocken zu halten, kommen Sie zu Benjamins Antwort:
wo es jetzt aussieht
Die Vorteile der zweiten Lösung sind, dass sie abstrahiert und trocken ist. Der Nachteil ist, dass Sie mehr Code haben und daran denken müssen, alle Ihre Versprechen zu reflektieren, um die Dinge konsistent zu machen.
Ich würde meine Lösung als explizit und KISS charakterisieren, aber in der Tat weniger robust. Die Benutzeroberfläche garantiert nicht, dass Sie genau wissen, ob das Versprechen erfolgreich war oder fehlgeschlagen ist.
Zum Beispiel könnten Sie dies haben:
Dies wird nicht durch erwischt werden
a.catch
, soEs gibt keine Möglichkeit zu sagen, welches tödlich war und welches nicht. Wenn dies wichtig ist, sollten Sie eine Schnittstelle erzwingen, die nachverfolgt, ob sie erfolgreich war oder nicht (was der
reflect
Fall ist).Wenn Sie Fehler nur ordnungsgemäß behandeln möchten, können Sie Fehler einfach als undefinierte Werte behandeln:
In meinem Fall muss ich den Fehler nicht kennen oder wissen, wie er fehlgeschlagen ist - es ist mir nur wichtig, ob ich den Wert habe oder nicht. Ich lasse die Funktion, die das Versprechen generiert, sich um die Protokollierung des spezifischen Fehlers kümmern.
Auf diese Weise kann der Rest der Anwendung den Fehler ignorieren, wenn er möchte, und ihn als undefinierten Wert behandeln, wenn er möchte.
Ich möchte, dass meine Funktionen auf hoher Ebene sicher ausfallen und mich nicht um die Details kümmern, warum ihre Abhängigkeiten fehlgeschlagen sind, und ich bevorzuge KISS gegenüber DRY, wenn ich diesen Kompromiss eingehen muss - weshalb ich mich letztendlich für die Nichtverwendung entschieden habe
reflect
.quelle
Promise
s. Während Siereflect
die Wiederverwendung von Code verbessern, wird auch eine andere Abstraktionsebene eingerichtet. Da Nathans Antwort bisher nur einen Bruchteil der Stimmen im Vergleich zu Ihrer erhalten hat, frage ich mich, ob dies ein Hinweis auf ein Problem mit seiner Lösung ist, das ich noch nicht erkannt habe.new Promise((res, rej) => res(new Error('Legitimate error'))
nicht zu unterscheiden vonnew Promise(((res, rej) => rej(new Error('Illegitimate error'))
? Oder könnten Sie nicht nach filternx.status
? Ich werde diesen Punkt zu meiner Antwort hinzufügen, damit der Unterschied klarer wirdPromise.all()
Variante verwendet wird. Außerdem muss der Promise-Verbraucher wissen, dass ein bestimmtes Versprechen nicht abgelehnt wird, sondern wird schluck es ist fehler. Tatsächlich könnte diereflect()
Methode durch das Aufrufen weniger "abstrakt" und expliziter gemacht werden.PromiseEvery(promises).then(...)
Die Komplexität der obigen Antwort im Vergleich zu Benjamins sollte viel über diese Lösung aussagen.In Vanille-Javascript gibt es einen fertigen Vorschlag für eine Funktion, die dies nativ erfüllen kann: Diese
Promise.allSettled
hat es in Stufe 4 geschafft, ist in ES2020 offiziell und in allen modernen Umgebungen implementiert . Es ist derreflect
Funktion in dieser anderen Antwort sehr ähnlich . Hier ist ein Beispiel von der Vorschlagseite. Vorher hätten Sie tun müssen:Wenn Sie
Promise.allSettled
stattdessen verwenden, entspricht das oben Gesagte:Benutzer moderner Umgebungen können diese Methode ohne Bibliotheken verwenden . In diesen sollte das folgende Snippet ohne Probleme ausgeführt werden:
Ausgabe:
Für älteren Browser gibt es einen spezifikationsgemäßen polyfill hier .
quelle
Ich mag Benjamins Antwort sehr und wie er im Grunde alle Versprechen in immer lösende, aber manchmal mit Fehlern als Ergebnis umwandelt. :)
Hier ist mein Versuch auf Ihre Anfrage, nur für den Fall, dass Sie nach Alternativen suchen. Diese Methode behandelt Fehler einfach als gültige Ergebnisse und ist ähnlich wie
Promise.all
sonst codiert :quelle
settle
. Wir haben das auch in Bluebird, ich mag es besser zu reflektieren, aber dies ist eine praktikable Lösung, wenn Sie dies für ein Array haben.Promise
Konstruktor richtig verwenden (und diesesvar resolve
Ding vermeiden )?Das
Promise.all
wird jedes abgelehnte Versprechen verschlucken und den Fehler in einer Variablen speichern, sodass es zurückkehrt, wenn alle Versprechen gelöst wurden. Dann können Sie den Fehler erneut beseitigen oder was auch immer tun. Auf diese Weise würden Sie wahrscheinlich die letzte Ablehnung anstelle der ersten herausholen.quelle
err.push(error)
so, als könnte dies Fehler aggregieren, indem es zu einem Array gemacht und verwendet wird , sodass alle Fehler in die Luft gesprudelt werden könnten.Ich hatte das gleiche Problem und habe es folgendermaßen gelöst:
In diesem Fall
Promise.all
wird auf jedes Versprechen gewartetresolved
oderrejected
eingegangen.Und mit dieser Lösung "stoppen wir die
catch
Ausführung" auf nicht blockierende Weise. Tatsächlich stoppen wir nichts, sondern geben nur dasPromise
in einem ausstehenden Zustand zurück, das ein anderes zurückgibt,Promise
wenn es nach dem Timeout behoben ist.quelle
Promise.all
. Ich suche nach einer Möglichkeit, zuzuhören, wenn alle Versprechen aufgerufen wurden, aber nicht selbst. Vielen Dank.all()
macht das, sie wartet auf die Erfüllung aller Versprechen oder die Ablehnung mindestens eines davon..all
alles ausgelöst wird.then
oder ein.all
Anruf), aber sie werden ausgeführt, wenn sie erstellt werden.Dies sollte mit der Vorgehensweise von Q übereinstimmen :
quelle
Die Antwort von Benjamin Gruenbaum ist natürlich großartig. Aber ich kann auch sehen, dass der Standpunkt von Nathan Hagen mit dem Abstraktionsgrad vage erscheint. Kurze Objekteigenschaften wie
e & v
helfen auch nicht, aber das könnte natürlich geändert werden.In Javascript gibt es ein Standardfehlerobjekt namens
Error
,. Idealerweise werfen Sie immer eine Instanz / einen Nachkommen davon. Der Vorteil ist, dass Sie dies tuninstanceof Error
können und wissen, dass etwas ein Fehler ist.Mit dieser Idee nehme ich das Problem auf.
Fangen Sie den Fehler grundsätzlich ab. Wenn der Fehler nicht vom Typ Fehler ist, schließen Sie den Fehler in ein Fehlerobjekt ein. Das resultierende Array enthält entweder aufgelöste Werte oder Fehlerobjekte, die Sie überprüfen können.
Die Instanz innerhalb des Catch ist für den Fall, dass Sie eine externe Bibliothek verwenden, die dies möglicherweise getan hat
reject("error")
, anstattreject(new Error("error"))
.Natürlich könnten Sie Versprechen haben, wenn Sie einen Fehler beheben, aber in diesem Fall wäre es höchstwahrscheinlich sinnvoll, ihn trotzdem als Fehler zu behandeln, wie das letzte Beispiel zeigt.
Ein weiterer Vorteil dabei ist, dass die Array-Zerstörung einfach gehalten wird.
Anstatt
Sie könnten argumentieren, dass die
!error1
Überprüfung einfacher ist als eine Instanz davon, aber Sie müssen auch beide zerstörenv & e
.quelle
Anstatt abzulehnen, lösen Sie es mit einem Objekt auf. Sie könnten so etwas tun, wenn Sie Versprechen umsetzen
quelle
Ich denke , die folgenden Angebote ein etwas anderer Ansatz ... Vergleichen
fn_fast_fail()
mitfn_slow_fail()
... obwohl letztere nicht als solche nicht ... Sie , wenn ein oder beide überprüfena
undb
ist eine InstanzError
undthrow
dass ,Error
wenn Sie wollen , es zu erreichen dercatch
Block (zBif (b instanceof Error) { throw b; }
). Siehe die jsfiddle .quelle
Hier ist mein Brauch
settledPromiseAll()
Verglichen mit
Promise.all
Wenn alle Versprechen erfüllt sind, funktioniert es genau wie das Standardversprechen.
Wenn eines oder mehrere Versprechen abgelehnt werden, gibt es das erste zurück, das ähnlich wie das Standardversprechen abgelehnt wurde, wartet jedoch im Gegensatz dazu darauf, dass alle Versprechen gelöst / abgelehnt werden.
Für die Mutigen könnten wir uns ändern
Promise.all()
:VORSICHT . Im Allgemeinen ändern wir niemals integrierte Funktionen, da dies andere nicht verwandte JS-Bibliotheken beschädigen oder mit zukünftigen Änderungen der JS-Standards in Konflikt geraten kann.
My
settledPromiseall
ist abwärtskompatibelPromise.all
und erweitert seine Funktionalität.Menschen, die Standards entwickeln - warum nicht in einen neuen Promise-Standard aufnehmen?
quelle
Promise.all
mit modernenasync/await
Ansatzquelle
Ich würde tun:
quelle
Sie können Ihre Logik nacheinander über nsynjs des synchronen Executors ausführen . Es wird bei jedem Versprechen angehalten, auf die Lösung / Ablehnung gewartet und entweder das Ergebnis der Auflösung der
data
Eigenschaft zugewiesen oder eine Ausnahme ausgelöst (für die Behandlung, für die Sie einen Try / Catch-Block benötigen). Hier ist ein Beispiel:quelle
Ich verwende seit ES5 folgende Codes.
Die Verwendungssignatur ist genau wie
Promise.all
. Der Hauptunterschied besteht darin, dassPromise.wait
auf alle Versprechen gewartet wird, um ihre Arbeit zu beenden.quelle
Ich weiß, dass diese Frage viele Antworten hat, und ich bin sicher, dass (wenn nicht alle) richtig sein müssen. Es war jedoch sehr schwer für mich, die Logik / den Ablauf dieser Antworten zu verstehen.
Also habe ich mir die ursprüngliche Implementierung angeschaut
Promise.all()
und versucht, diese Logik nachzuahmen - mit der Ausnahme, dass die Ausführung nicht gestoppt wird, wenn ein Versprechen fehlschlägt.Erläuterung:
- Durchlaufen Sie die Eingabe
promisesList
und führen Sie jedes Versprechen aus.- Egal, ob das Versprechen aufgelöst oder abgelehnt wurde: Speichern Sie das Ergebnis des Versprechens in einem
result
Array gemäß demindex
. Speichern Sie auch den Auflösungs- / Ablehnungsstatus (isSuccess
).- Wenn alle Versprechen abgeschlossen sind, geben Sie ein Versprechen mit dem Ergebnis aller anderen zurück.
Anwendungsbeispiel:
quelle
Promise.all
, da zu viele Dinge schief gehen. Ihre Version verarbeitet beispielsweise keine leeren Eingaben.Ich weiß nicht, welche Versprechungsbibliothek Sie verwenden, aber die meisten haben so etwas wie allSettled .
Bearbeiten: Ok, da Sie einfaches ES6 ohne externe Bibliotheken verwenden möchten, gibt es keine solche Methode.
Mit anderen Worten: Sie müssen Ihre Versprechen manuell durchlaufen und ein neues kombiniertes Versprechen auflösen, sobald alle Versprechen erfüllt sind.
quelle