Ein Einblick in neue Methoden für Promises

Avatar of Jeremias Menichelli
Jeremias Menichelli am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

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.