Promises sind eines der am meisten gefeierten Features, die in JavaScript eingeführt wurden. Das Vorhandensein eines nativen asynchronen Artefakts direkt in der Sprache hat eine neue Ära eingeläutet, die nicht nur die Art und Weise, wie wir Code schreiben, verändert hat, sondern auch die Grundlage für andere tolle APIs gelegt hat – wie fetch!
Lassen Sie uns einen Moment zurücktreten, um die Funktionen zu rekapitulieren, die wir bei ihrer ersten Veröffentlichung erhalten haben, und welche neuen Extras wir als nächstes bekommen.
Neu im Konzept von Promises? Ich empfehle wärmstens Jake Archibalds Artikel als Einführung.
Heutige Features
Werfen wir einen kurzen Blick auf einige der Dinge, die wir derzeit mit Promises tun können. Als JavaScript sie einführte, gab es uns eine API zur Ausführung asynchroner Aktionen und zur Reaktion auf deren erfolgreiche Rückgabe oder Fehler, eine Möglichkeit, eine Assoziation um Daten oder Ergebnisse zu schaffen, deren Wert wir noch nicht kennen.
Hier sind die Promise-Features, die wir heute haben.
Promise-Handling
Jedes Mal, wenn eine asynchrone Methode ein Promise zurückgibt – wie bei der Verwendung von fetch – können wir then() verwenden, um Aktionen auszuführen, wenn das Promise *erfüllt* ist, und catch(), um auf ein *abgelehntes* Promise zu reagieren.
fetch('//resource.to/some/data')
.then(result => console.log('we got it', result.json()))
.catch(error => console.error('something went wrong', error))
Der klassische Anwendungsfall ist das Abrufen von Daten aus einer API und entweder das Laden der Daten, wenn sie zurückkehren, oder die Anzeige einer Fehlermeldung, wenn die Daten nicht gefunden werden konnten.
Darüber hinaus erhielten wir bei der Erstveröffentlichung zwei Methoden zur Behandlung von Promise-Gruppen.
Auflösen und Ablehnen von Promise-Sammlungen
Ein Promise kann *erfüllt* sein, wenn es erfolgreich aufgelöst wurde, *abgelehnt*, wenn es mit einem Fehler aufgelöst wurde, und *ausstehend*, solange keine Auflösung erfolgt ist. Ein Promise gilt als *settled* (abgeschlossen), wenn es aufgelöst wurde, unabhängig vom Ergebnis.
Daher gibt es zwei Methoden, die uns bei der Behandlung einer Gruppe von Promises helfen, abhängig von der Kombination der Zustände, die wir erhalten.
Promise.all ist eine dieser Methoden. Es wird nur erfüllt, wenn alle Promises erfolgreich aufgelöst wurden, und gibt ein Array mit dem Ergebnis für jedes einzelne zurück. Wenn eines der Promises fehlschlägt, geht Promise.all zu catch und gibt den Grund des Fehlers zurück.
Promise.all([
fetch('//resource.to/some/data'),
fetch('//resource.to/more/data')
])
.then(results => console.log('We got an array of results', results)
.catch(error => console.error('One of the promises failed', error)
In diesem Fall wird Promise.all frühzeitig abgebrochen und zu catch geleitet, sobald eines der Mitglieder der Sammlung einen Fehler auslöst, oder es wird abgeschlossen, wenn alle Promises *erfüllt* sind.
Schauen Sie sich diesen kurzen Text von Domenic Denicola über Promise-Zustände an, um eine detailliertere Erklärung der Terminologie und Konzepte zu erhalten.
Wir haben auch Promise.race, das sofort mit dem ersten Promise aufgelöst wird, das es zurückerhält, egal ob es erfüllt oder abgelehnt wurde. Nachdem das erste Promise aufgelöst wurde, werden die restlichen ignoriert.
Promise.race([
fetch('//resource.to/some/data'),
fetch('//resource.to/other/data')
])
.then(result => console.log('The first promise was resolved', result))
.catch(reason => console.error('One of the promises failed because', reason))
Die Neulinge
Okay, wir wenden uns nun den neuen Promise-Funktionen zu, auf die wir uns freuen können.
Promise.allSettled
Die nächste vorgeschlagene Ergänzung der Familie ist Promise.allSettled, die, wie der Name schon sagt, erst fortfährt, wenn alle Elemente der Sammlung im Array nicht mehr im ausstehenden Status sind, egal ob sie *abgelehnt* oder *erfüllt* wurden.
Promise.allSettled([
fetch('//resource.to/some/data'),
fetch('//resource.to/more/data'),
fetch('//resource.to/even/more/data')
])
.then(results => {
const fulfilled = results.filter(r => r.status === 'fulfilled')
const rejected = results.filter(r => r.status === 'rejected')
})
Beachten Sie, wie sich dies von Promise.all unterscheidet, da wir niemals in die catch-Anweisung eintreten. Das ist sehr gut, wenn wir auf Datensätze warten, die in verschiedene Teile einer Webanwendung gehen, aber für jedes Ergebnis spezifischere Nachrichten anzeigen oder unterschiedliche Aktionen ausführen möchten.
Promise.any
Die nächste neue Methode ist Promise.any, die es uns ermöglicht, auf jedes erfüllte Promise in einer Sammlung zu reagieren, aber erst dann frühzeitig abbricht, wenn *alle* fehlgeschlagen sind.
Promise.any([
fetch('//resource.to/some/data'),
fetch('//resource.to/more/data'),
fetch('//resource.to/even/more/data')
])
.then(result => console.log('a batch of data has arrived', result))
.catch(() => console.error('all promises failed'))
Das ist so etwas wie Promise.race, nur dass Promise.race beim ersten Auflösen frühzeitig abbricht. Wenn also das erste Promise im Satz mit einem Fehler aufgelöst wird, fährt Promise.race fort. Promise.any wartet weiter, bis die restlichen Elemente im Array aufgelöst sind, bevor es fortfährt.
Demo
Einige davon sind mit einer visuellen Darstellung leichter zu verstehen, daher habe ich ein kleines Playground zusammengestellt, das die Unterschiede zwischen den neuen und bestehenden Methoden zeigt.
Zusammenfassung
Obwohl sie sich noch im Entwurfsstadium befinden, gibt es Community-Skripte, die die in diesem Beitrag behandelten neuen Methoden emulieren. Dinge wie Bluebird's any und reflect sind gute Polyfills, während wir auf eine verbesserte Browserunterstützung warten.
Sie zeigen auch, wie die Community bereits diese Art von asynchronen Mustern nutzt, aber deren integrierte Unterstützung wird die Möglichkeiten für neue Muster beim Abrufen von Daten und der asynchronen Auflösung für Webanwendungen eröffnen.
Neben then und catch können Sie finally an ein Promise anfügen. Sarah Drasner hat einen detaillierten Artikel darüber geschrieben, den Sie sich ansehen können.
Wenn Sie mehr über die bevorstehenden Promise-Kombinatoren erfahren möchten, hat der V8-Blog gerade eine kurze Erklärung mit Links zur offiziellen Spezifikation und den Vorschlägen veröffentlicht.
Promise.finally() ist ebenfalls eine sehr nützliche Funktion.
Sie ist praktisch, wenn Sie doppelten Code in .then() und .catch() haben. Indem Sie diesen Teil in die finally()-Anweisung verschieben, können Sie ihn DRY (Don't Repeat Yourself) halten.
Ja,
finallyist ein nützlicher Handler. Dieser Artikel konzentriert sich jedoch mehr auf Promise-Kombinatoren und nicht auf Handler. Außerdem hat Sarah bereits hier auf CSS Tricks darüber geschrieben. Ich werde im Artikel eine Erwähnung hinzufügen. Danke für den Kommentar!@jmenichelli: Woher erfahren Sie von den kommenden Funktionen im Voraus?
Bitte lassen Sie mich wissen, wenn Sie von solchen Seiten und Blogs wissen, auf denen ich solche Dinge ebenfalls überprüfen kann.
Es hängt davon ab, wie Sie Informationen über Webtechnologien konsumieren. Ich benutze Twitter viel, also folge ich dem V8-Team-Account und ihren Ingenieuren, um herauszufinden, woran sie gerade arbeiten, iterieren oder in Chrome implementieren. Es gibt auch RSS-Feeds und Newsletter, die von Mitgliedern der Community kuratiert werden und die Sie abonnieren und per E-Mail erhalten können.
Ich kann in der Spezifikation nicht finden, was passieren würde, wenn man Promise.allSettled().catch() verwendet. Müssen Fehler in then() behandelt werden? FolktaleJS bietet eine Task als Promise-Abstraktion mit einer waitAny()-Schnittstelle, die wartet, bis alle Tasks abgeschlossen sind, bevor die ersten Fehler oder das Array der aufgelösten Task-Rückgabewerte zurückgegeben werden. Ist das dasselbe wie Promise.allSettled()?
Das fand ich auch knifflig. In meinen ersten Tests geht es immer zu
thenals Teil des Arrays, Sie können später filtern und diereason-Eigenschaft untersuchen. Ich weiß nicht, ob ein Fehler, der geworfen wird, abgesehen von demPromise, das den Reject-Handler aufruft, Sie zu catch schicken könnte, ich vermute, das wird nicht passieren.