Geschrieben von Rosario De Chiara✏️
In JavaScript sind Promises ein leistungsstarkes Tool zur Verarbeitung asynchroner Vorgänge, das besonders nützlich bei UI-bezogenen Ereignissen ist. Sie stellen einen Wert dar, der möglicherweise nicht sofort verfügbar ist, aber irgendwann in der Zukunft behoben wird.
Versprechen ermöglichen (oder sollten) Entwicklern, saubereren, besser verwaltbaren Code zu schreiben, wenn sie Aufgaben wie API-Aufrufe, Benutzerinteraktionen oder Animationen erledigen. Durch die Verwendung von Methoden wie .then(), .catch() und .finally() ermöglichen Promises eine intuitivere Möglichkeit, mit Erfolgs- und Fehlerszenarien umzugehen und die berüchtigte „Callback-Hölle“ zu vermeiden.
In diesem Artikel verwenden wir die neue Promise.withResolvers()-Methode (März 2024), mit der Sie saubereren und einfacheren Code schreiben können, indem Sie ein Objekt zurückgeben, das drei Dinge enthält: ein neues Promise und zwei Funktionen, eine zum Auflösen des Promise und die andere, um es abzulehnen. Da es sich um ein aktuelles Update handelt, benötigen Sie eine aktuelle Node-Laufzeitumgebung (v>22), um die Beispiele in diesem Artikel auszuführen.
In den beiden folgenden funktional äquivalenten Codeabschnitten können wir den alten Ansatz und den neuen Ansatz vergleichen, der Methode zuzuweisen, ein Versprechen entweder aufzulösen oder abzulehnen:
let resolve, reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); Math.random() > 0.5 ? resolve("ok") : reject("not ok");
Im Code oben sehen Sie die traditionellste Verwendung eines Promise: Sie instanziieren ein neues Promise-Objekt und müssen dann im Konstruktor die beiden Funktionen „resolve“ und „reject“ zuweisen, die wann aufgerufen werden benötigt.
Im folgenden Codeausschnitt wurde derselbe Codeabschnitt mit der neuen Promise.withResolvers()-Methode neu geschrieben und sieht einfacher aus:
const { promise, resolve, reject } = Promise.withResolvers(); Math.random() > 0.5 ? resolve("ok") : reject("not ok");
Hier können Sie sehen, wie der neue Ansatz funktioniert. Es gibt das Promise zurück, auf dem Sie die Methode .then() und die beiden Funktionen „resolve“ und „reject“ aufrufen können.
Der traditionelle Ansatz für Versprechen fasst die Erstellungs- und Ereignisverarbeitungslogik in einer einzigen Funktion zusammen, was einschränkend sein kann, wenn mehrere Bedingungen oder verschiedene Teile des Codes das Versprechen auflösen oder ablehnen müssen.
Im Gegensatz dazu bietet Promise.withResolvers() eine größere Flexibilität, indem es die Erstellung des Promises von der Auflösungslogik trennt, wodurch es für die Verwaltung komplexer Bedingungen oder mehrerer Ereignisse geeignet ist. Für einfache Anwendungsfälle kann die traditionelle Methode jedoch einfacher und vertrauter für diejenigen sein, die mit Standard-Versprechungsmustern vertraut sind.
Wir können den neuen Ansatz nun an einem realistischeren Beispiel testen. Im folgenden Code sehen Sie ein einfaches Beispiel für einen API-Aufruf:
function fetchData(url) { return new Promise((resolve, reject) => { fetch(url) .then(response => { // Check if the response is okay (status 200-299) if (response.ok) { return response.json(); // Parse JSON if response is okay } else { // Reject the promise if the response is not okay reject(new Error('API Invocation failed')); } }) .then(data => { // Resolve the promise with the data resolve(data); }) .catch(error => { // Catch and reject the promise if there is a network error reject(error); }); }); } // Example usage const apiURL = '<ADD HERE YOU API ENDPOINT>'; fetchData(apiURL) .then(data => { // Handle the resolved data console.log('Data received:', data); }) .catch(error => { // Handle any errors that occurred console.error('Error occurred:', error); });
Die fetchData-Funktion ist so konzipiert, dass sie eine URL annimmt und ein Promise zurückgibt, das einen API-Aufruf mithilfe der Fetch-API verarbeitet. Es verarbeitet die Antwort, indem es prüft, ob der Antwortstatus im Bereich von 200–299 liegt, was auf einen Erfolg hinweist.
Bei Erfolg wird die Antwort als JSON geparst und das Promise mit den resultierenden Daten aufgelöst. Wenn die Antwort nicht erfolgreich ist, wird das Promise mit einer entsprechenden Fehlermeldung abgelehnt. Darüber hinaus umfasst die Funktion eine Fehlerbehandlung, um Netzwerkfehler abzufangen und das Promise abzulehnen, wenn ein solcher Fehler auftritt.
Das Beispiel zeigt, wie diese Funktion verwendet wird. Es zeigt, wie die aufgelösten Daten mit einem .then()-Block verwaltet und Fehler mithilfe eines .catch()-Blocks behandelt werden, um sicherzustellen, dass sowohl ein erfolgreicher Datenabruf als auch Fehler ordnungsgemäß verwaltet werden.
Im folgenden Code schreiben wir die Funktion fetchData() neu, indem wir die neue Methode Promise.withResolvers() verwenden:
function fetchData(url) { const { promise, resolve, reject } = Promise.withResolvers(); fetch(url) .then(response => { // Check if the response is okay (status 200-299) if (response.ok) { return response.json(); // Parse JSON if response is okay } else { // Reject the promise if the response is not okay reject(new Error('API Invocation failed')); } }) .then(data => { // Resolve the promise with the data resolve(data); }) .catch(error => { // Catch and reject the promise if there is a network error reject(error); }); return promise; }
Wie Sie sehen können, ist der obige Code besser lesbar und die Rolle des Objekts Promise ist klar: Die fetchData-Funktion gibt ein Promise zurück, das erfolgreich aufgelöst wird oder fehlschlägt, und ruft in jedem Fall die richtige Methode auf . Sie finden den Code oben im Repository mit dem Namen api.invocation.{old|new}.js.
Der folgende Code untersucht, wie eine Promise-Stornierungsmethode implementiert wird. Wie Sie vielleicht wissen, können Sie ein Versprechen in JavaScript nicht stornieren. Versprechen stellen das Ergebnis eines asynchronen Vorgangs dar und sind so konzipiert, dass sie nach ihrer Erstellung aufgelöst oder abgelehnt werden, ohne dass ein integrierter Mechanismus zum Abbrechen vorhanden ist.
Diese Einschränkung entsteht, weil Promises einen definierten Zustandsübergangsprozess haben; Sie beginnen mit dem Status „Ausstehend“ und können, sobald sie erledigt sind, ihren Status nicht mehr ändern. Sie sollen das Ergebnis einer Operation kapseln und nicht die Operation selbst steuern, was bedeutet, dass sie den zugrunde liegenden Prozess nicht beeinflussen oder abbrechen können. Diese Designwahl hält Promises einfach und konzentriert sich auf die Darstellung des letztendlichen Ergebnisses einer Operation:
const cancellablePromise = () => { const { promise, resolve, reject } = Promise.withResolvers(); promise.cancel = () => { reject("the promise got cancelled"); }; return promise; };
In the code above, you can see the object named cancellablePromise, which is a promise with an additional cancel() method that, as you can see, simply forces the invocation of the reject method. This is just syntactic sugar and does not cancel a JavaScript Promise, though it may help in writing clearer code.
An alternative approach is to use an AbortController and AbortSignal, which can be tied to the underlying operation (e.g., an HTTP request) to cancel it when needed. From the documentation, you can see that the AbortController and AbortSignal approach is a more expressive implementation of what we implemented in the code above: once the AbortSignal is invoked, the promise just gets rejected.
Another approach is to use reactive programming libraries like RxJS, which offers an implementation of the Observable pattern, a more sophisticated control over async data streams, including cancellation capabilities.
When speaking about practical use cases, Promises are well-suited for handling single asynchronous operations, such as fetching data from an API. In contrast, Observables are ideal for managing streams of data, such as user input, WebSocket events, or HTTP responses, where multiple values may be emitted over time.
We already clarified that once initiated, Promises cannot be canceled, whereas Observables allow for cancellation by unsubscribing from the stream. The general idea is that, with Observables, you have an explicit structure of the possible interaction with the object:
This is demonstrated in the code below:
import { Observable } from 'rxjs'; const observable = new Observable(subscriber => { subscriber.next(1); subscriber.next(2); subscriber.next(3); subscriber.complete(); }); const observer = observable.subscribe({ next(x) { console.log('Received value:', x); }, complete() { console.log('Observable completed'); } }); observer.unsubscribe();
This code cannot be rewritten with Promises because the Observable returns three values while a Promise can only be resolved once.
To experiment further with the unsubscribe method, we can add another Observer that will use the takeWhile() method: it will let the Observer wait for values to match a specific condition; in the code below, for example, it keeps receiving events from the Observable while the value is not 2:
import { Observable, takeWhile } from 'rxjs'; const observable = new Observable(subscriber => { subscriber.next(1); subscriber.next(2); subscriber.next(3); subscriber.complete(); }); const observer1 = observable.subscribe({ next(x) { console.log('Received by 1 value:', x); }, complete() { console.log('Observable 1 completed'); } }); const observer2 = observable.pipe( takeWhile(value => value != "2") ).subscribe(value => console.log('Received by 2 value:', value));
In the code above, observer1 is the same as we have already seen: it will just subscribe and keep receiving all the events from the Observable. The second one, observer2, will receive elements from the Observable while the condition is matched. In this case, this means when the value is different from 2.
From the execution, you can see how the two different mechanisms work:
$ node observable.mjs Received by 1 value: 1 Received by 1 value: 2 Received by 1 value: 3 Observable 1 completed Received by 2 value: 1 $
In this article, we investigated the new mechanism to allocate a Promise in JavaScript and laid out some of the possible ways to cancel a Promise before its completion. We also compared Promises with Observable objects, which not only offer the features of Promises but extend them by allowing multiple emissions of events and a proper mechanism for unsubscribing.
Debugging code is always a tedious task. But the more you understand your errors, the easier it is to fix them.
LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to see exactly what the user did that led to an error.
LogRocket records console logs, page load times, stack traces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!
Try it for free.
Das obige ist der detaillierte Inhalt vonBeherrschen der Versprechensaufhebung in JavaScript. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!