Ich habe meinen Code in Versprechen umstrukturiert und eine wundervolle lange flache Versprechenskette aufgebaut , die aus mehreren .then()
Rückrufen besteht. Am Ende möchte ich einen zusammengesetzten Wert zurückgeben und muss auf mehrere Zwischenversprechen zugreifen . Die Auflösungswerte aus der Mitte der Sequenz sind jedoch im letzten Rückruf nicht im Geltungsbereich. Wie kann ich darauf zugreifen?
function getExample() {
return promiseA(…).then(function(resultA) {
// Some processing
return promiseB(…);
}).then(function(resultB) {
// More processing
return // How do I gain access to resultA here?
});
}
javascript
, ist sie in einer anderen Sprache relevant. Ich benutze nur die Antwort "Break the Chain" in Java und jdeferredAntworten:
Frei kämpfen
Wenn Sie auf die Zwischenwerte in Ihrer Kette zugreifen müssen, sollten Sie Ihre Kette in die einzelnen Teile aufteilen, die Sie benötigen. Anstatt einen Rückruf anzuhängen und irgendwie zu versuchen, seinen Parameter mehrmals zu verwenden, fügen Sie mehrere Rückrufe demselben Versprechen hinzu - wo immer Sie den Ergebniswert benötigen. Vergessen Sie nicht, ein Versprechen repräsentiert nur (Proxies) einen zukünftigen Wert ! Verwenden Sie neben dem Ableiten eines Versprechens vom anderen in einer linearen Kette die Versprechen-Kombinatoren, die Sie von Ihrer Bibliothek erhalten, um den Ergebniswert zu erstellen.
Dies führt zu einem sehr einfachen Kontrollfluss, einer klaren Zusammensetzung der Funktionen und damit zu einer einfachen Modularisierung.
Anstelle der Parameterzerstörung im Rückruf, die danach
Promise.all
erst mit ES6 verfügbar wurde, wurde derthen
Aufruf in ES5 durch eine raffinierte Hilfsmethode ersetzt, die von vielen Versprechensbibliotheken bereitgestellt wurde ( Q , Bluebird , when ,…) :.spread(function(resultA, resultB) { …
.Bluebird bietet auch eine spezielle
join
Funktion , um diesePromise.all
+spread
Kombination durch ein einfacheres (und effizienteres) Konstrukt zu ersetzen :quelle
promiseA
undpromiseB
sind die (Versprechen zurückgebenden) Funktionen hier.spread
war in diesem Muster sehr nützlich. Für modernere Lösungen siehe die akzeptierte Antwort. Ich habe jedoch bereits die explizite Passthrough-Antwort aktualisiert , und es gibt wirklich keinen guten Grund, diese nicht ebenfalls zu aktualisieren.ECMAScript Harmony
Natürlich wurde dieses Problem auch von den Sprachdesignern erkannt. Sie haben viel Arbeit geleistet und der Vorschlag für asynchrone Funktionen hat es endlich geschafft
ECMAScript 8
Sie benötigen keine einzige
then
Aufruf- oder Rückruffunktion mehr, da Sie in einer asynchronen Funktion (die beim Aufruf ein Versprechen zurückgibt) einfach darauf warten können, dass Versprechen direkt aufgelöst werden. Es enthält auch beliebige Kontrollstrukturen wie Bedingungen, Schleifen und Try-Catch-Klauseln, aber der Einfachheit halber brauchen wir sie hier nicht:ECMAScript 6
Während wir auf ES8 warteten, verwendeten wir bereits eine sehr ähnliche Syntax. ES6 wurde mit Generatorfunktionen geliefert , mit denen die Ausführung bei willkürlich platzierten
yield
Schlüsselwörtern in Teile zerlegt werden kann . Diese Slices können unabhängig voneinander und sogar asynchron nacheinander ausgeführt werden - und genau das tun wir, wenn wir auf eine Versprechen-Lösung warten möchten, bevor wir den nächsten Schritt ausführen.Es gibt dedizierte Bibliotheken (wie co oder task.js ), aber auch viele Versprechensbibliotheken verfügen über Hilfsfunktionen ( Q , Bluebird , when ,…), die diese asynchrone schrittweise Ausführung für Sie ausführen , wenn Sie ihnen eine Generatorfunktion geben gibt Versprechen.
Dies funktionierte in Node.js seit Version 4.0, auch einige Browser (oder deren Entwicklungseditionen) unterstützten die Generatorsyntax relativ früh.
ECMAScript 5
Wenn Sie jedoch abwärtskompatibel sein möchten / müssen, können Sie diese nicht ohne Transpiler verwenden. Sowohl Generatorfunktionen als auch Async-Funktionen werden vom aktuellen Tool unterstützt, siehe zum Beispiel die Dokumentation von Babel zu Generatoren und Async-Funktionen .
Und dann gibt es noch viele andere Compile-to-JS-Sprachen , die sich der Vereinfachung der asynchronen Programmierung widmen. Sie verwenden in der Regel eine Syntax ähnlich wie
await
(zB Iced Coffeescript ), aber es gibt auch andere , die eine Haskell-ähnliche Funktiondo
-Notation (zB LatteJs , monadische , PURESCRIPT oder LispyScript ).quelle
getExample
handelt sich immer noch um eine Funktion, die ein Versprechen zurückgibt und genau wie die Funktionen in den anderen Antworten funktioniert, jedoch mit einer besseren Syntax. Sie könntenawait
eine andereasync
Funktion aufrufen oder.then()
mit dem Ergebnis verketten.steps.next().value.then(steps.next)...
aber das hat nicht funktioniert.Synchrone Inspektion
Zuweisen von Versprechungen für später benötigte Werte zu Variablen und anschließendes Abrufen ihres Werts durch synchrone Überprüfung. Das Beispiel verwendet die
.value()
Methode von bluebird, aber viele Bibliotheken bieten eine ähnliche Methode an.Dies kann für beliebig viele Werte verwendet werden:
quelle
Verschachtelung (und) Verschlüsse
Die Verwendung von Verschlüssen zur Aufrechterhaltung des Variablenumfangs (in unserem Fall der Parameter der Erfolgsrückruffunktion) ist die natürliche JavaScript-Lösung. Mit Versprechungen können wir Rückrufe willkürlich verschachteln und
.then()
reduzieren - sie sind semantisch äquivalent, mit Ausnahme des Umfangs des inneren.Natürlich baut dies eine Einrückungspyramide. Wenn die Einrückung zu groß wird, können Sie trotzdem die alten Werkzeuge anwenden, um der Pyramide des Untergangs entgegenzuwirken : Modularisieren, zusätzliche benannte Funktionen verwenden und die Versprechenskette reduzieren, sobald Sie keine Variable mehr benötigen.
Theoretisch können Sie immer mehr als zwei Verschachtelungsebenen vermeiden (indem Sie alle Verschlüsse explizit machen). In der Praxis können Sie so viele verwenden, wie sinnvoll sind.
Sie können auch Hilfsfunktionen für diese Art der Teilanwendung verwenden , z. B.
_.partial
von Underscore / lodash oder der nativen.bind()
Methode , um die Einrückung weiter zu verringern:quelle
bind
Funktion in Monaden. Haskell bietet syntaktischen Zucker (Do-Notation), damit er wie eine asynchrone / wartende Syntax aussieht.Expliziter Durchgang
Ähnlich wie beim Verschachteln der Rückrufe beruht diese Technik auf Schließungen. Die Kette bleibt jedoch flach - anstatt nur das neueste Ergebnis zu übergeben, wird für jeden Schritt ein Statusobjekt übergeben. Diese Statusobjekte sammeln die Ergebnisse der vorherigen Aktionen und geben alle Werte, die später erneut benötigt werden, sowie das Ergebnis der aktuellen Aufgabe weiter.
Hier ist dieser kleine Pfeil
b => [resultA, b]
die Funktion, die geschlossenresultA
wird und ein Array beider Ergebnisse an den nächsten Schritt übergibt. Womit die Syntax der Parameterdestrukturierung verwendet wird, um sie wieder in einzelne Variablen aufzuteilen.Bevor die Destrukturierung mit ES6 verfügbar wurde,
.spread()
wurde von vielen Versprechensbibliotheken ( Q , Bluebird , when ,…) eine raffinierte Hilfsmethode bereitgestellt . Es ist erforderlich, dass eine Funktion mit mehreren Parametern - einer für jedes Array-Element - als verwendet wird.spread(function(resultA, resultB) { …
.Natürlich kann dieser hier benötigte Verschluss durch einige Hilfsfunktionen weiter vereinfacht werden, z
Alternativ können Sie
Promise.all
das Versprechen für das Array erstellen:Und Sie können nicht nur Arrays verwenden, sondern auch beliebig komplexe Objekte. Zum Beispiel mit
_.extend
oderObject.assign
in einer anderen Hilfsfunktion:Während dieses Muster eine flache Kette garantiert und explizite Zustandsobjekte die Klarheit verbessern können, wird es für eine lange Kette mühsam. Besonders wenn Sie den Staat nur sporadisch brauchen, müssen Sie ihn dennoch durch jeden Schritt führen. Mit dieser festen Schnittstelle sind die einzelnen Rückrufe in der Kette ziemlich eng gekoppelt und können sich nicht ändern. Dadurch wird das Ausklammern einzelner Schritte erschwert, und Rückrufe können nicht direkt von anderen Modulen bereitgestellt werden. Sie müssen immer in Code gekoppelt werden, der sich um den Status kümmert. Abstrakte Helferfunktionen wie die oben genannten können den Schmerz ein wenig lindern, sind aber immer vorhanden.
quelle
Promise.all
, gefördert werden sollte (es funktioniert in ES6 nicht, wenn die Destrukturierung es ersetzt und das Umschalten auf a.spread
zuthen
oft unerwarteten Ergebnissen führt. Ich bin mir nicht sicher, warum Sie das brauchen Gebrauch Augmentation - Dinge zu dem Versprechen Prototyp Zugabe ist keine akzeptable Art und Weise ES6 Versprechungen sowieso , die mit (die derzeit nicht unterstützt) Subklassen erweitert werden sollen , zu verlängern.Promise.all
"? Keine der Methoden in dieser Antwort wird mit ES6 brechen. Der Wechsel von aspread
zu einer Destrukturierungthen
sollte ebenfalls keine Probleme haben. Re .prototype.augment: Ich wusste, dass jemand es bemerken würde, ich habe nur gern Möglichkeiten erkundet - ich werde es herausarbeiten.return [x,y]; }).spread(...
stattdessenreturn Promise.all([x, y]); }).spread(...
würde sich dies nicht ändern, wenn der Spread gegen es6-destrukturierenden Zucker getauscht wird, und es wäre auch kein seltsamer Randfall, in dem Versprechen zurückkehrende Arrays anders behandeln als alles andere.Veränderlicher Kontextzustand
Die triviale (aber unelegante und eher fehleranfällige) Lösung besteht darin, nur Variablen mit höherem Gültigkeitsbereich (auf die alle Rückrufe in der Kette Zugriff haben) zu verwenden und ihnen Ergebniswerte zu schreiben, wenn Sie sie erhalten:
Anstelle vieler Variablen kann auch ein (anfangs leeres) Objekt verwendet werden, auf dem die Ergebnisse als dynamisch erstellte Eigenschaften gespeichert werden.
Diese Lösung hat mehrere Nachteile:
Die Bluebird-Bibliothek empfiehlt die Verwendung eines weitergegebenen Objekts, indem mit ihrer
bind()
Methode ein Kontextobjekt einer Versprechen-Kette zugewiesen wird. Auf sie kann von jeder Rückruffunktion über das ansonsten unbrauchbarethis
Schlüsselwort zugegriffen werden . Während Objekteigenschaften anfälliger für unentdeckte Tippfehler als für Variablen sind, ist das Muster ziemlich clever:Dieser Ansatz kann leicht in Versprechungsbibliotheken simuliert werden, die .bind nicht unterstützen (obwohl etwas ausführlicher und nicht in einem Ausdruck verwendet werden kann):
quelle
.bind()
ist nicht notwendig, um Speicherverlust zu verhindernEin weniger harter Dreh zu "Mutable Contextual State"
Die Verwendung eines Objekts mit lokalem Gültigkeitsbereich zum Sammeln der Zwischenergebnisse in einer Versprechenskette ist ein vernünftiger Ansatz für die von Ihnen gestellte Frage. Betrachten Sie das folgende Snippet:
quelle
Promise
Konstruktor-Antimuster !Knoten 7.4 unterstützt jetzt asynchrone / wartende Anrufe mit dem Harmony-Flag.
Versuche dies:
und führen Sie die Datei aus mit:
node --harmony-async-await getExample.js
So einfach wie möglich!
quelle
In diesen Tagen habe ich auch einige Fragen wie Sie getroffen. Endlich finde ich eine gute Lösung mit der Frage, es ist einfach und gut zu lesen. Ich hoffe das kann dir helfen.
Nach How-to-Chain-Javascript-Versprechen
ok, schauen wir uns den Code an:
quelle
.then
zu erhalten, sondern das Ergebnis davor. ZBthirdPromise
Zugriff auf das Ergebnis vonfirstPromise
.Eine andere Antwort mit
babel-node
Version <6Verwenden von
async - await
npm install -g [email protected]
example.js:
Dann
babel-node example.js
renn und voila!quelle
Ich werde dieses Muster nicht in meinem eigenen Code verwenden, da ich kein großer Fan der Verwendung globaler Variablen bin. Zur Not wird es jedoch funktionieren.
Benutzer ist ein versprochenes Mungomodell.
quelle
globalVar
, oderUser.findAsync({}).then(function(users){ console.log(users); mongoose.connection.close() });
?Eine andere Antwort mit sequentiellem Executor nsynjs :
Update: Arbeitsbeispiel hinzugefügt
quelle
Bei Verwendung von Bluebird können Sie mithilfe der
.bind
Methode Variablen in der Versprechenskette freigeben:Bitte überprüfen Sie diesen Link für weitere Informationen:
http://bluebirdjs.com/docs/api/promise.bind.html
quelle
einfacher Weg: D.
quelle
Ich denke, Sie können Hash von RSVP verwenden.
So etwas wie unten:
quelle
Promise.all
Lösung , nur mit einem Objekt anstelle eines Arrays.Lösung:
Sie können Zwischenwerte in jeder späteren Funktion 'then' explizit in den Gültigkeitsbereich einfügen, indem Sie 'bind' verwenden. Es ist eine nette Lösung, bei der die Funktionsweise von Promises nicht geändert werden muss und nur ein oder zwei Codezeilen erforderlich sind, um die Werte weiterzugeben, genau wie Fehler bereits weitergegeben werden.
Hier ist ein vollständiges Beispiel:
Diese Lösung kann wie folgt aufgerufen werden:
(Hinweis: Eine komplexere und vollständigere Version dieser Lösung wurde getestet, nicht jedoch diese Beispielversion, sodass möglicherweise ein Fehler vorliegt.)
quelle
async
/await
Bedeutet noch Versprechungen verwenden. Was Sie möglicherweise aufgeben, sindthen
Anrufe mit Rückrufen.Was ich über Versprechen lerne, ist, sie nur zu verwenden, wenn Rückgabewerte möglichst nicht auf sie verweisen . Die asynchrone / warten-Syntax ist dafür besonders praktisch. Heute unterstützen es alle neuesten Browser und Knoten: https://caniuse.com/#feat=async-functions , ist ein einfaches Verhalten und der Code ist wie das Lesen von synchronem Code, vergessen Sie Rückrufe ...
In Fällen, in denen ich auf ein Versprechen verweisen muss, erfolgt die Erstellung und Lösung an unabhängigen / nicht verwandten Orten. Stattdessen bevorzuge ich eine künstliche Assoziation und wahrscheinlich einen Ereignis-Listener, um das "entfernte" Versprechen zu lösen. Ich ziehe es vor, das Versprechen als verzögert darzustellen, was der folgende Code in gültigem es5 implementiert
transpiliert von einem Typoskript-Projekt von mir:
https://github.com/cancerberoSgx/misc-utils-of-mine/blob/2927c2477839f7b36247d054e7e50abe8a41358b/misc-utils-of-mine-generic/src/promise.ts#L31
Für komplexere Fälle verwende ich oft diese kleinen Versprechungsdienstprogramme ohne Abhängigkeiten, die getestet und getippt wurden. p-map war schon mehrmals nützlich. Ich denke, er hat die meisten Anwendungsfälle abgedeckt:
https://github.com/sindresorhus?utf8=%E2%9C%93&tab=repositories&q=promise&type=source&language=
quelle