Wenn Sie schon länger in JavaScript programmieren, sind Sie wahrscheinlich schon auf Promises gestoßen.
Aber verstehen Sie sie wirklich auf Senior-Niveau? Wenn nicht, könnten Sie auf zufällige Fehler stoßen, deren Ursache nur schwer zu finden ist, oder Sie wissen möglicherweise nicht, wie man Promises mit funktionalen Mustern kombiniert.
Egal, ob Sie Anfänger oder erfahrener Entwickler sind, der begierig darauf ist, einige fortgeschrittene Tipps zu lernen, dieser Artikel ist vollgepackt mit Codebeispielen und Best Practices, um Ihnen zu helfen, Promises zu meistern.
Wenn Sie eher ein visueller Lerntyp sind, sehen Sie sich das YouTube-Video zu diesem Thema unten an:
Beginnen wir also mit ...
Ein Promise ist ein Objekt, das die eventuelle Fertigstellung oder das Scheitern einer asynchronen Operation darstellt. Stellen Sie es sich als Platzhalter für einen zukünftigen Wert vor. Anstatt den endgültigen Wert sofort zurückzugeben, gibt eine asynchrone Methode ein Promise zurück, um den Wert zu einem späteren Zeitpunkt zu liefern.
Sie können ein Promise mit dem Promise -Konstruktor erstellen, der eine Executor-Funktion benötigt. Diese Executor-Funktion akzeptiert zwei Argumente:
resolve: Eine Funktion, die aufgerufen wird, wenn das Promise erfüllt ist.reject: Eine Funktion, die aufgerufen wird, wenn das Promise abgelehnt wird.So erstellen Sie ein auflösendes Promise:
const resolvingPromise = new Promise(resolve => {
resolve('Hello, world!');
});Man verwendet das new Schlüsselwort, um ein Promise zu erstellen. Der Konstruktor akzeptiert einen Callback, die sogenannte Executor-Funktion. Der erste Parameter dieser Executor-Funktion ist die resolve Funktion. Man übergibt ihr den Wert, auf den das Promise aufgelöst werden soll, wenn der Vorgang erfolgreich abgeschlossen wurde, in diesem Fall 'Hello, world!'.
Es ist entscheidend, zwischen dem Promise-Konstruktor und der Promise-Instanz zu unterscheiden. Der Promise-Konstruktor bezieht sich auf die Promise Klasse (nach dem new Schlüsselwort), während eine Promise-Instanz ein Objekt ist, das mit dem Promise Konstruktor erstellt wurde, hier resolvingPromise.
Und hier ist ein ablehnendes Promise:
const rejectingPromise = new Promise((_, reject) => {
reject(new Error('Something went wrong'));
});Im rejectingPromiseerstellt man ein neues Promise, das sofort mit einer Fehlermeldung abgelehnt wird. Indem man (_, reject) zur Executor-Funktion konzentrieren Sie sich auf die `reject`-Funktion, die Sie mit einem Fehlerobjekt aufrufen. Sie verwenden einen Unterstrich _ um den resolve Parameter im ablehnenden Promise zu ignorieren, da Sie ihn nicht benötigen.
Promises sind verkettbar, wodurch Sie Sequenzen asynchroner Operationen auf lesbare Weise verarbeiten können. Das Promise -Objekt bietet drei Hauptmethoden:
then: Hängt Callbacks für die Auflösung oder Ablehnung des Promise an. Es gibt ein neues Promise zurück, was eine weitere Verkettung ermöglicht.catch: Hängt einen Callback zur Behandlung von Fehlern oder Ablehnungen in der Promise-Kette an. Es gibt ebenfalls ein neues Promise zurück.finally: Hängt einen Callback an, der unabhängig vom Status des Promise (erfüllt oder abgelehnt) ausgeführt wird. Er wird für Aufräumaktionen verwendet und gibt ebenfalls ein neues Promise zurück.thenDie then Methode wird verwendet, um den aufgelösten Wert einer Promise zu verarbeiten. Sie nimmt eine Callback-Funktion entgegen, die den aufgelösten Wert als Argument erhält.
const resolvingPromise = new Promise(resolve => resolve('Hello, world!'));
myPromise.then(result => {
console.log(result); // Output: Hello, world!
});Erstellen Sie eine Promise, die sich sofort auflöst mit 'Hello, world!'. Als Nächstes hängen Sie einen then Handler an die Promise an. Die then Methode empfängt den aufgelösten Wert und protokolliert ihn in der Konsole.
Sie können auch abgelehnte Promises mit then verarbeiten, indem Sie den zweiten Parameter verwenden.
const rejectingPromise = new Promise((_, reject) =>
reject(new Error('Something went wrong')),
);
rejectingPromise.then(null, error => {
console.error(error); // Output: Error: Something went wrong
});Erstellen Sie erneut eine ablehnende Promise.
Wenn Sie nun thenaufrufen, ist der erste Parameter null , da Sie keinen Erfüllungs-Handler haben. Der zweite Parameter ist ebenfalls eine Callback-Funktion und erhält den Ablehnungsgrund als Argument.
Dies ermöglicht es Ihnen, sowohl Erfüllung als auch Ablehnung in derselben then-Methode zu verarbeiten.
Da die then -Methode eine Higher-Order-Funktion ist, können Sie Argumente im Point-Free-Stil übergeben.
const resolvingPromise = new Promise(resolve => resolve('Hello, world!'));
resolvingPromise.then(console.log); // Output: Hello, world!Erstellen Sie ein Promise, das sich sofort auflöst.
Fügen Sie einen then -Handler an das Promise an. Indem Sie console.log direkt an thenübergeben, verwenden Sie den Point-Free-Stil. Der aufgelöste Wert wird automatisch als Argument an console.logübergeben.
catchDie catch-Methode ist speziell für die Behandlung abgelehnter Promises gedacht, wodurch Ihr Code sauberer und lesbarer wird.
const rejectingPromise = new Promise((_, reject) =>
reject(new Error('Something went wrong')),
);
rejectingPromise.catch(error => {
console.error(error); // Output: Error: Something went wrong
});Erstellen Sie ein ablehnendes Promise und fügen Sie einen catch -Handler an rejectingPromisean. Die Callback-Funktion empfängt das Fehlerobjekt und protokolliert es in der Konsole. Dies ist eine direktere Methode zur Fehlerbehandlung im Vergleich zur Verwendung des zweiten Parameters von then.
finallyDie finally Methode führt einen Callback aus, unabhängig davon, ob das Promise erfüllt oder abgelehnt wurde, was für Aufräumarbeiten nützlich ist.
const resolvingPromise = new Promise(resolve => resolve('Hello, world!'));
resolvingPromise
.then((result) => {
console.log(result);
})
.finally(() => {
console.log('Promise settled');
});Erstellen Sie ein auflösendes Promise.
Fügen Sie einen then Handler an das Promise an. Die Callback-Funktion empfängt den aufgelösten Wert und protokolliert ihn in der Konsole.
Als Nächstes fügen Sie einen finally Handler an das Promise an. Der finally Block wird ausgeführt, nachdem das Promise abgeschlossen ist, unabhängig vom Ergebnis.
Wenn Sie Promises verketten, gibt es einige Verhaltensweisen, die Sie beachten sollten.
then Handlern: Das Werfen eines Fehlers in einem then -Handler führt dazu, dass das Promise abgelehnt wird, wobei nachfolgende then -Handler übersprungen werden, bis ein catch gefunden wird.catch -Handler oder dem zweiten Parameter von thenabgefangen werden.then -Handlern fortsetzen.Lassen Sie uns anhand einiger Beispiele untersuchen, wie sich Promise-Chaining verhält.
const trace = message => value => {
console.log(message, value);
return value;
};
new Promise(resolve => resolve(1))
.then(trace('first'))
.then(trace('second'));Definieren Sie eine Curried- trace Funktion, die eine Nachricht entgegennimmt und eine andere Funktion zurückgibt. Diese zurückgegebene Funktion nimmt einen Wert, protokolliert die Nachricht und den Wert und gibt dann den Wert zurück.
Erstellen Sie als Nächstes ein Promise, das sich sofort mit 1auflöst. Hängen Sie zwei then -Handler an das Promise an. Jeder then -Handler erhält den aufgelösten Wert 1, protokolliert ihn zusammen mit einer Nachricht und gibt ihn an den nächsten Handler weiter.
Wenn Sie diesen Code ausführen, sehen Sie:
$ node first-chain-example.js
first 1
second 1Sehen wir uns nun an, was passiert, wenn Sie in einem der then -Handler einen Fehler werfen.
const trace = message => value => {
console.log(message, value);
return value;
};
const throwError = error => {
throw new Error(error);
};
new Promise(resolve => resolve(2))
.then(throwError)
.then(trace('never gets logged out'))
.catch(trace('caught'));Definieren Sie die trace Funktion erneut und eine throwError Funktion, die einen Wert entgegennimmt und einen neuen Error mit diesem Wert auslöst.
Erstellen Sie ein Promise, das sich sofort mit 2. Hängen Sie einen then Handler an, der throwError, der einen Fehler auslöst, wodurch das Promise abgelehnt wird. Der nächste then Handler wird übersprungen, da das Promise sich nun in einem abgelehnten Zustand befindet. Die Kontrolle geht an den nächsten catch Handler über.
Wenn Sie diesen Code ausführen, sehen Sie:
$ node second-chain-example.js
caught Error: 2Der zweite then Handler wird nicht ausgeführt, weil Fehler dazu führen, dass die Promise-Kette direkt zum nächsten überspringt catch.
Nachdem ein Fehler abgefangen wurde, können Sie weiterhin then -Handler verketten.
const trace = message => value => {
console.log(message, value);
return value;
};
new Promise((_, reject) => reject(3))
.then(trace('never gets logged out'))
.catch(x => trace('caught')(x + 1))
.catch(trace('previous catch handles this - nothing gets logged out'))
.then(trace('gets the result from catch'));Beginnen Sie mit einem Promise, das mit 3abgelehnt wird. Hängen Sie dann nacheinander einen then -Handler, zwei catch -Handler und schließlich einen weiteren then -Handler an.
Wenn Sie diesen Code ausführen, werden Sie sehen:
$ node third-chain-example.js
caught 4
gets the result from catch 4Der anfängliche then -Handler wird übersprungen, weil das Promise abgelehnt wird. Der erste catch Handler erhält den Ablehnungsgrund 3, erhöht ihn um 1, protokolliert ihn und gibt den neuen Wert zurück 4. Das Promise ist nun aufgelöst mit 4.
Alle nachfolgenden catch Handler werden übersprungen, da das Promise aufgelöst ist. Der nächste then Handler erhält den Wert 4.
Beachten Sie, dass Sie den Wert, der durch die Kette geleitet wird, in jedem then oder catch Handler, wie Sie es hier getan haben, indem Sie den Wert um 1.
Nun, machen wir noch ein letztes komplexes Beispiel mit mehreren Ablehnungen.
const trace = message => value => {
console.log(message, value);
return value;
};
const throwError = error => {
throw new Error(error);
};
new Promise(resolve => resolve(4))
.then(trace('first'))
.catch(trace('nothing to catch - never gets logged out'))
.then(trace('second'))
.then(x => x + 1)
.then(trace("keep in mind you can change what's being passed through"))
.then(throwError)
.then(null, error => trace('caught through second parameter')(error))
.catch(trace('previous .then handles this - nothing gets logged out'))
.then(trace('this is the end'));Lösen Sie ein Promise auf mit 4. Hängen Sie dann einen then Handler daran, einen catch Handler, 5 weitere then Handler, wobei der letzte den zweiten Parameter von then verwendet, um Fehler abzufangen, einen weiteren catch Handler und schließlich einen weiteren then Handler.
Wenn Sie diesen Code ausführen, sehen Sie:
$ node fourth-chain-example.js
first 4
second 4
keep in mind you can change what's being passed through 5
caught through second parameter Error: 5
this is the end 5Hier ist, was Schritt für Schritt passiert:
4.then: Protokolliert 'first 4' und gibt zurück 4.catch: Wird übersprungen, da kein Fehler vorliegt.then: Protokolliert 'second 4' und gibt zurück 4.dann erhöht den Wert auf 5.dann protokolliert "Denken Sie daran, Sie können ändern, was durch 5 übergeben wird" und gibt 5zurück.dann löst einen Fehler mit dem Wert aus 5.then: Das nachfolgende then fängt den Fehler mit dem zweiten Parameter ab, protokolliert 'Fehler über zweiten Parameter abgefangen: 5', und gibt 5zurück.catch: Übersprungen, da der Fehler bereits behandelt wurde.then: Protokolliert 'dies ist das Ende 5'.Ein Promise kann sich in einem von drei Zuständen befinden:
onFulfilled() Funktion aus, die typischerweise mit einem resolve() Aufruf im Promise-Behandlungscode verbunden ist.onRejected() Funktion aus, die im Allgemeinen an einen reject() Aufruf gebunden ist.Sobald ein Promise von "pending" zu "fulfilled" oder "rejected" wechselt, wird es "settled" (abgeschlossen). Das bedeutet, es wurde aufgelöst (entweder erfüllt oder abgelehnt) und wird seinen Zustand nicht mehr ändern. Jeder weitere Versuch, ein abgeschlossenes Promise aufzulösen oder abzulehnen, hat keine Wirkung, wodurch seine Unveränderlichkeit erhalten bleibt.
Es gibt keine Eigenschaft, die Ihnen den internen Promise-Zustand anzeigt. Sie können die Zustände eines Promise nur indirekt beobachten, indem Sie den Promise Konstruktor verwenden und das Promise-Objekt inspizieren.
const pendingPromise = new Promise((resolve, reject) => {
console.log('Promise state:', pendingPromise); // Output: Promise { <pending> }
setTimeout(() => {
resolve('Fulfilled!');
console.log('Promise state after resolve:', pendingPromise); // Output: Promise { 'Fulfilled!' }
}, 1000);
});
pendingPromise.then(value => {
console.log('Resolved with:', value); // Output: Resolved with: Fulfilled!
});Erstellen pendingPromise, die sich anfänglich im Status „pending“ befindet.
Protokollieren Sie als Nächstes pendingPromise sofort, was Folgendes anzeigt: Promise { <pending> }. Nach 1 Sekunde lösen Sie die Promise mit 'Fulfilled!'.
Wenn Sie jetzt pendingPromise mithilfe von thenprotokollieren, wird Promise { 'Fulfilled!' } nach 1 Sekunde angezeigt.
waitForSie können das Gelernte miteinander verbinden und eine waitFor Funktion.
const waitFor = (value, ms) =>
new Promise((resolve, reject) => {
setTimeout(() => {
if (value) {
return resolve(value);
}
return reject(new Error('Value is falsy'));
}, timeout);
});
waitFor(true, 1000).then(() => console.log('Done!'));Diese waitFor Funktion nimmt einen Wert und ein Timeout entgegen. Sie wartet auf das Timeout. Wenn der Wert dann 'truthy' ist, wird das Promise aufgelöst. Andernfalls wird es abgelehnt.
Wichtig: Verwenden Sie immer return vor resolve und reject innerhalb Ihrer if- und else-Blöcke. Ohne return könnte die Funktion weiter ausgeführt werden und möglicherweise sowohl resolve als auch reject aufrufen, was zu unerwartetem Verhalten führt.
Promises sind 'eager'. 'Eager' beschreibt das Auswertungsverhalten von Operationen. Es gibt zwei verschiedene Auswertungsstrategien: 'eager' und 'lazy'.
Eager Evaluation berechnet Werte, sobald sie zugewiesen werden. Lazy Evaluation verzögert die Berechnung, bis der Wert benötigt wird.
Das bedeutet für Promises Folgendes:
console.log("Before promise");
const myPromise = new Promise(resolve => {
console.log("Inside promise");
resolve("Promise resolved");
});
console.log("After promise");
myPromise.then(console.log);Wenn Sie diesen Code ausführen, sehen Sie die folgende Ausgabe:
Before promiseInside promiseAfter promisePromise resolved
Before promise
Inside promise
After promise
Promise resolved
Beachten Sie, dass "Inside promise" direkt nach "Before promise" protokolliert wird, noch bevor wir einen then-Handler anhängen. Dies demonstriert die 'eager'-Natur von Promises.
Wenn Sie ein Beispiel für 'lazy evaluation' sehen möchten, sehen Sie sich an "JavaScript Generators Explained, But On A Senior-Level".
JavaScript bietet mehrere statische Methoden auf dem Promise-Objekt, um mehrere Promises und andere Szenarien zu verwalten.
Promise.resolveEs gibt eine weitere Möglichkeit, ein Promise ohne Funktion zu erstellen. Sie können zwei statische Methoden auf dem Promise -Objekt verwenden.
Die erste ist Promise.resolve, die Sie verwenden können, um ein Promise zu erstellen, das sich sofort mit einem gegebenen Wert auflöst.
const promise1 = new Promise(resolve => resolve('Hello, world!'));
const promise2 = Promise.resolve('Hello, world!');
promise2.then((message) => {
console.log(message); // Output: Hello, world!
});Hier sind promise1 und promise2 äquivalent.
Promise.rejectEs gibt auch Promise.reject, mit dem Sie ein Promise erstellen können, das sofort mit einem gegebenen Grund abgelehnt wird.
const promise3 = new Promise( (_, reject) => reject(new Error('Something went wrong')));const promise4 = Promise.reject(new Error('Something went wrong'));promise4.catch(error => { console.error(error); // Output: Error: Something went wrong});
const promise3 = new Promise(
(_, reject) => reject(new Error('Something went wrong'))
);
const promise4 = Promise.reject(new Error('Something went wrong'));
promise4.catch(error => {
console.error(error); // Output: Error: Something went wrong
});Da das Promise nun abgelehnt wird, müssen Sie catch (oder den zweiten Parameter von then) um das Ergebnis zu erhalten. Wieder gilt: promise3 und promise4 sind äquivalent.
Promise.allWartet, bis alle Promises in einem iterierbaren Objekt aufgelöst sind, und gibt ein neues Promise zurück, das zu einem Array ihrer Ergebnisse aufgelöst wird.
const wait = (ms, value) =>
new Promise(resolve => setTimeout(() => resolve(value), ms));
const promises = [wait(2000, 'Hello'), wait(1000, 'World')];
Promise.all(promises).then(results => {
console.log(results); // Output: ['Hello', 'World']
});Erstellen Sie eine Hilfsfunktion wait die einen Wert und ein Timeout entgegennimmt. Sie gibt ein Promise zurück, das nach dem Timeout aufgelöst wird.
Als Nächstes erstellen Sie ein Array promises mit zwei Promises. Eines wird nach 2 Sekunden aufgelöst und das andere nach 1 Sekunde.
Danach rufen Sie Promise.all mit promises. Sie gibt eine neue Promise zurück, die aufgelöst wird, wenn alle Promises im Array aufgelöst wurden. Der aufgelöste Wert ist ein Array der Ergebnisse der Eingabe-Promises, in diesem Fall ['Hello', 'World']. Da die erste Promise langsamer ist und nach 2 Sekunden aufgelöst wird, dauert es zwei Sekunden, bis die von Promise.all mit dem Array aufgelöst wird.
Promise.allSettledDie Promise.allSettled Methode wartet, bis alle Promises abgeschlossen sind, d.h. sie wurden entweder erfüllt oder abgelehnt. Sie gibt eine neue Promise zurück, die mit einem Array von Objekten aufgelöst wird, die das Ergebnis jeder Promise beschreiben.
const promise1 = Promise.resolve('Success');
const promise2 = Promise.reject('Error');
Promise.allSettled([promise1, promise2]).then((results) => {
results.forEach((result) => console.log(result));
});
// Output:
// { status: 'fulfilled', value: 'Success' }
// { status: 'rejected', reason: 'Error' }Erstellen Sie zwei Promises: eine, die aufgelöst wird, und eine, die abgelehnt wird. Wenn Sie Promise.allSettledverwenden, wartet sie, bis beide Promises abgeschlossen sind, und protokolliert dann ein Array mit den Ergebnissen. Jedes Ergebnisobjekt enthält eine status Eigenschaft, die angibt, ob die Promise erfüllt oder abgelehnt wurde, und entweder einen value oder einen reason mit dem Ergebnis.
Diese Methode ist nützlich, wenn Sie mehrere Promises verarbeiten und alle Ergebnisse bearbeiten möchten, unabhängig davon, ob sie erfolgreich waren oder fehlgeschlagen sind.
Promise.anyDie Promise.any -Methode gibt ein Promise zurück, das erfüllt wird, sobald eines der Promises im Iterable erfüllt wird. Wenn alle Promises abgelehnt werden, wird es mit einem AggregateErrorabgelehnt.
const promise1 = Promise.reject('Error 1');
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'Success'));
const promise3 = Promise.reject('Error 3');
Promise.any([promise1, promise2, promise3])
.then((value) => {
console.log(value); // Output: Success
})
.catch((error) => {
console.error(error);
});Erstellen Sie zwei ablehnende Promises und eines, das nach 100 Millisekunden erfüllt wird. Promise.any wartet darauf, dass das erste Promise erfüllt wird. Da promise2 nach 100 Millisekunden erfüllt wird, protokolliert der then -Handler 'Success'. Wenn Sie den Code so ändern, dass alle Promises abgelehnt werden, wird der catch -Block einen AggregateErrorbehandeln, was darauf hinweist, dass alle Promises abgelehnt wurden.
Promise.raceDie Promise.race Methode nimmt ein Array von Promises entgegen und gibt ein Promise zurück, das sich auflöst, sobald sich eines der Promises im Iterable auflöst (erfüllt oder abgelehnt).
const promise1 = new Promise((resolve) => setTimeout(resolve, 500, 'First'));
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'Second'));
const promise3 = new Promise((_, reject) => setTimeout(reject, 50, 'Error'));
Promise.race([promise1, promise2, promise3])
.then((value) => {
console.log('Resolved with:', value);
})
.catch((error) => {
console.error('Rejected with:', error); // Output after 50ms: Rejected with: Error
});Erstellen Sie zwei Promises, die sich nach unterschiedlichen Zeitverzögerungen auflösen, und eines, das nach 50 Millisekunden abgelehnt wird. Da promise3 am schnellsten abgelehnt wird, Promise.race löst sich sofort mit dieser Ablehnung auf.
Sie können die Zeitverzögerungen anpassen, um zu sehen, wie Promise.race sich verhält, wenn sich das zuerst aufgelöste Promise stattdessen erfüllt.
const promise1 = new Promise((resolve) => setTimeout(resolve, 50, 'First'));
const promise2 = new Promise((resolve) => setTimeout(resolve, 500, 'Second'));
const promise3 = new Promise((_, reject) => setTimeout(reject, 100, 'Error'));
Promise.race([promise1, promise2, promise3])
.then((value) => {
console.log('Resolved with:', value);
})
.catch((error) => {
console.error('Rejected with:', error); // Output after 50ms: Rejected with: Error
});Ändern Sie die Zeitverzögerungen so, dass promise1 sich nach 50 Millisekunden auflöst und promise2 nach 500 Millisekunden. Und promise3 jetzt nach 100 Millisekunden abgelehnt wird. Da promise1 sich am schnellsten erfüllt, Promise.race wird aufgelöst mit 'Erste'. promise3 wird immer noch abgelehnt, aber mit Promise.race spielt das keine Rolle.
async / awaitDie async und await Schlüsselwörter bieten eine besser lesbare und synchroner wirkende Methode, um mit Promises zu arbeiten.
async: Deklariert eine asynchrone Funktion, die ein Promise zurückgibt.await: Hält die Ausführung einer asynchronen Funktion an, bis ein Promise erfüllt ist.Sie können jede Funktionsdeklaration asynchron machen, indem Sie das async Schlüsselwort vor dem Schlüsselwort function. Und Sie können Pfeilfunktionen asynchron machen, indem Sie das async Schlüsselwort vor den Klammern der Parameterliste.
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
};
fetchData().then((data) => {
console.log(data);
});Definieren Sie eine asynchrone Funktion fetchData , die Daten von einer API abruft. Mit awaitunterbrechen Sie die Ausführung, bis das Fetch-Promise aufgelöst ist. Dasselbe tun Sie für den response.json() Aufruf. Wenn Sie fetchDataaufrufen, können Sie .then verwenden, um die zurückgegebenen Daten zu verarbeiten, da das async Schlüsselwort die Funktion dazu bringt, ein Promise zurückzugeben.
awaitNormalerweise müssen Sie Funktionen als asynchron um nutzen zu können erwarten in ihnen. In einigen JavaScript-Umgebungen können Sie jedoch Folgendes verwenden erwarten in nicht asynchronen Funktionen. Diese Funktion wird als „Top-Level“ bezeichnet erwarten„.
const result = await Promise.resolve('Hello, world!');
console.log(result); // Output: Hello, world!Mit Top-Level erwarten, du kannst direkt auf Zusagen warten. In diesem Fall müssen Sie also nicht verwenden dann um das Ergebnis abzumelden.
Du kannst erwarten synchrone Werte wie Zeichenketten, Zahlen, Boolesche Werte und Arrays.
asynchrone Funktion awaitSynchronous () {const result = warte auf 'Hallo, Welt! ' ; Ergebnis zurückgeben;} awaitSynchronous () .then (console.log);//Ausgabe: Hallo, Welt!
async function awaitSynchronous() {
const result = await 'Hello, world!';
return result;
}
awaitSynchronous().then(console.log); // Output: Hello, world!Schreiben Sie eine Funktion Warte synchron das wartet auf eine Schnur. Da du den benutzt asynchron Schlüsselwort, die Funktion wird zu einer Promise-Rückgabefunktion.
Dann du erwarten die Zeichenfolge, die nichts bewirkt, da sich alle „non-promise“ -Werte genauso verhalten, wie sie es normalerweise in synchronem Code tun.
versuche... fangeBei der Verwendung asynchron / erwarten, Sie können Fehler behandeln mit versuche... fange, wodurch Ihr asynchroner Code sauberer wird.
const fetchData = async () => {
throw new Error('Network error');
};
const getData = async () => {
try {
const data = await fetchData();
console.log(data);
} catch (error) {
console.error('Error:', error.message); // Output: Error: Network error
}
return 'No error'
};
getData().then(console.log); // Output: No errorDefinieren Sie eine Funktion Daten abrufen das wirft einen neuen Fehler aus. Jetzt kannst du verwenden Daten abrufen in einer anderen Funktion namens Daten abrufen, was die Ausführung von umschließt Daten abrufen in einem versuche... fange blockieren, um mögliche Fehler zu behandeln. Wenn du jetzt anrufst Daten abrufen, es wird immer noch behoben, aber melden Sie den Fehler aus dem Catch-Block in der Konsole ab, bevor Sie den aufgelösten Wert über den dann Methode.
Rückkehr erwartenIn asynchronen Funktionen sehen Sie möglicherweise Rückkehr erwarten vor einem potenziell ablehnenden Versprechen. Die Funktion würde zwar ohne funktionieren erwarten, seine Verwendung kann bei der Fehlerbehandlung helfen.
const getNumberWithAwait = async () => {
try {
return await Promise.reject(new Error('Oops!'));
} catch (error) {
console.error('Caught in getNumberWithAwait:', error.message);
}
};
const getNumberWithoutAwait = async () => {
try {
return Promise.reject(new Error('Oops!'));
} catch (error) {
console.error('Caught in getNumberWithoutAwait:', error.message);
}
};
getNumberWithAwait().then((number) => {
console.log('Result with await:', number); // Output: Result with await: undefined
});
getNumberWithoutAwait().then((number) => {
console.log('Result without await:', number);
}).catch((error) => {
console.error('Caught outside:', error.message); // Output: Caught outside: Oops!
});Sie definieren zwei Funktionen, die beide mit einem Fehler ablehnen.
Zuerst Nummer mit Await abrufen, welches verwendet erwarten vor dem Rückkehr Schlüsselwort.
Und zweitens Nummer ohne Wartezeit abrufen, was nicht benutzt erwarten vor dem Rückkehr Schlüsselwort.
Wenn du jetzt beide anrufst, wirst du sehen, dass Nummer mit Await abrufen löst sich auf, während Nummer ohne Wartezeit abrufen lehnt ab. Verwenden erwarten vor dem Rückkehr stellt sicher, dass alle Fehler innerhalb der versuche... fange blockieren.
Wenn Sie sich also mit Funktionen befassen, die Versprechen zurückgeben, können Sie feststellen, dass es drei Möglichkeiten gibt, ein Versprechen zu erstellen.
const asyncInc1 = x => new Promise(resolve => resolve(x + 1));
const asyncInc2 = x => Promise.resolve(x + 1);
const asyncInc3 = async x => x + 1;Asynchron C1 erstellt manuell ein neues Promise und löst es mit lösen.Async INC 2 Verwendungszwecke Versprich es. Löse für eine präzisere und unmittelbarere Lösung.Async in C3 definiert eine asynchrone Funktion, die implizit ein Versprechen zurückgibt, das zum Ergebnis aufgelöst wird.Die drei asyncInc Funktionen sind hinsichtlich ihres Verhaltens und ihrer Rückgabewerte äquivalent. Sie alle geben ein Promise zurück, das mit x um 1 inkrementiert wird.
Beachten Sie, dass asyncInc3 das await Keyword nicht innerhalb der Funktion verwendet. Es gibt trotzdem ein Promise zurück, wegen des async Keywords. Wie bereits erwähnt, hat das await Keyword keine Auswirkung auf synchrone Werte.
Die then Methode entpackt den Wert eines Promise. Das bedeutet, dass, wenn Sie ein Promise aus einem then Callback, wird es entpackt, sodass der nächste then in der Kette den aufgelösten Wert erhält, nicht das Promise selbst.
const inc = x => x + 1;
const asyncInc = async x => x + 1;
Promise.resolve(20).then(inc).then(console.log); // Output: 21
Promise.resolve(20).then(asyncInc).then(console.log); // Output: 21Definieren Sie zwei Funktionen inc und asyncInc.
Ganz gleich, ob Sie die then -Methode mit einer normalen Funktion inc oder einer „Promise-Lifting“-Funktion asyncIncaufrufen, die ihr Ergebnis in den Promise-Kontext hebt, wird der Wert entpackt und an das nächste then in der Kette weitergegeben. Ohne dieses Entpacken würde man erwarten, dass das zweite Beispiel Promise { <pending> }zurückgibt.
Das await -Schlüsselwort funktioniert genauso.
async function example() { const result = await Promise.resolve(20).then(inc); return result;}example().then(console.log); // Output: 21
async function example() {
const result = await Promise.resolve(20).then(inc);
return result;
}
example().then(console.log); // Output: 21Promise.resolve(20).then(inc) gibt ein Promise zurück, das sich zu 21auflöst. Das await -Schlüsselwort entpackt dann diesen Wert, sodass example den Wert 21zurückgibt.
Promises selbst verfügen über keine integrierte Möglichkeit, nach ihrer Initiierung abgebrochen zu werden.
Manche Leute versuchen, die Promise.race -Methode als Abbruchmechanismus zu missbrauchen. Das sieht dann etwa so aus:
function fetchData() {
const fetchPromise = fetch('https://api.example.com/data').then(response =>
response.json(),
);
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout after 5 seconds')), 5000),
);
return Promise.race([fetchPromise, timeoutPromise]);
}
fetchData()
.then(data => console.log(data))
.catch(error => console.error(error));Promise.race() wird verwendet, um das fetchPromise gegen ein timeoutPromise. Wenn der Fetch-Vorgang nicht innerhalb von 5 Sekunden abgeschlossen wird, wird das timeoutPromise abgelehnt, wodurch der Fetch-Vorgang effektiv abgebrochen wird.
Dieser Ansatz ist problematisch, da er die Kontrolle über den Abbruch von der Funktion wegnimmt, die das Promise ursprünglich erstellt hat. Diese Funktion ist typischerweise der ideale Ort für Bereinigungsaufgaben wie das Stoppen von Timeouts oder das Freigeben von Speicherressourcen. Wenn die Abbruchkontrolle extern erfolgt, werden diese wichtigen Bereinigungsaktivitäten möglicherweise nicht ordnungsgemäß durchgeführt.
Im Codebeispiel zum Abbrechen von Promises mit Race gibt es, wenn das Timeout zuerst ausgelöst wird, keine Möglichkeit, den Fetch-Vorgang tatsächlich daran zu hindern, im Hintergrund fortzufahren, was potenziell Ressourcen verschwendet.
Sie können die zugrunde liegenden asynchronen Operationen von Promises steuern, auch wenn Promises selbst keinen integrierten Abbruchmechanismus besitzen. Zum Beispiel können Sie einen AbortController verwenden, um eine Fetch-Anfrage an einen Server abzubrechen.
const controller = new AbortController();
const signal = controller.signal;
const fetchData = () => {
return fetch('https://api.example.com/data', { signal })
.then((response) => response.json());
};
const dataPromise = fetchData();
setTimeout(() => {
controller.abort();
}, 100);
dataPromise
.then((data) => {
console.log('Data:', data);
})
.catch((error) => {
if (error.name === 'AbortError') {
console.error('Fetch aborted');
} else {
console.error('Fetch error:', error);
}
});Erstellen Sie einen Abort-Controller und ein Signal.
Verwenden Sie AbortController , um die Fetch-Anfrage nach 100 Millisekunden abzubrechen, wodurch es wahrscheinlich ist, dass der Server noch nicht geantwortet hat. Während das Promise selbst nicht abgebrochen wird, wird die zugrunde liegende Operation abgebrochen, und das Promise wird mit einem AbortErrorabgelehnt. Sie können diesen Fehler behandeln, um zu erkennen, dass der Vorgang absichtlich abgebrochen wurde.
Vor Promises bedeutete die Handhabung von asynchronem Code oft die Auseinandersetzung mit der Callback-Hölle – verschachtelte Callbacks, die den Code schwer lesbar und wartbar machten. Promises bieten eine sauberere, robustere Methode zur Handhabung asynchroner Operationen.
function getUser(userId, callback) {
setTimeout(() => {
if (!userId) return callback('Invalid user ID');
callback(null, { id: userId, name: 'John Doe' });
}, 100);
}
function getPosts(user, callback) {
setTimeout(() => {
if (!user) return callback('User not found');
callback(null, [{ id: 1, title: 'Post 1' }]);
}, 100);
}
function getComments(post, callback) {
setTimeout(() => {
if (!post) return callback('Post not found');
callback(null, ['Comment 1', 'Comment 2']);
}, 100);
}
getUser(userId, (error, user) => {
if (error) {
console.error('Error fetching user:', error);
return;
}
getPosts(user, (error, posts) => {
if (error) {
console.error('Error fetching posts:', error);
return;
}
getComments(posts[0], (error, comments) => {
if (error) {
console.error('Error fetching comments:', error);
return;
}
console.log('First comment:', comments[0]);
});
});
});Erstellen Sie drei Funktionen getUser, getPosts, und getComments die asynchrone Operationen mit Callbacks simulieren.
Um die Daten abzurufen, müssen Sie nun die Callbacks verschachteln. Dieser Code wird aufgrund der verschachtelten Callbacks und der wiederholten Fehlerbehandlung unübersichtlich und schwer nachvollziehbar. Entwickler nannten dies nicht umsonst die „Callback-Hölle“.
Dies lässt sich bereinigen, indem man Promises verwendet, um die asynchronen Operationen auf eine flache und lesbare Weise zu verketten.
// ... original callback-based functions ...
const promisify =
fn =>
(...args) =>
new Promise((resolve, reject) => {
fn(...args, (error, result) => {
if (error) {
return reject(error);
}
resolve(result);
});
});
const getUserAsync = promisify(getUser);
const getPostsAsync = promisify(getPosts);
const getCommentsAsync = promisify(getComments);
getUserAsync(userId)
.then(user => getPostsAsync(user))
.then(posts => getCommentsAsync(posts[0]))
.then(comments => {
console.log('First comment:', comments[0]);
})
.catch(error => {
console.error('Error:', error);
});Schreiben Sie zunächst eine promisify Funktion, um Callback-basierte Funktionen in Promise-zurückgebende Funktionen umzuwandeln.
Verwenden Sie dann die promisify Funktion, um Ihre Callback-basierten Funktionen in Promise-zurückgebende Funktionen umzuwandeln. Dadurch können Sie sie mit then, was zu saubererem und wartbarerem Code führt. Die Fehlerbehandlung wird durch einen einzigen Catch-Block am Ende vereinfacht.
asyncPipeWenn Sie gelesen haben "Unleash JavaScript's Potential With Functional Programming", wissen Sie, dass Sie Funktionen miteinander verketten können. Falls nicht, sollten Sie diesen Artikel lesen, bevor Sie mit dem Rest dieses Artikels fortfahren, da er davon ausgeht, dass Sie mit der Funktionskomposition vertraut sind.
In diesem Artikel lernen Sie die pipe Funktion kennen, die Funktionen in umgekehrter mathematischer Reihenfolge zusammensetzt.
const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x);Diese pipe Funktion kann nicht mit Promises verwendet werden, da Promises die then Methode benötigen.
Aber Sie können eine asyncPipe Funktion schreiben, die Promises verketten kann.
const asyncPipe = (...fns) => x => fns.reduce((y, f) => y.then(f), Promise.resolve(x));
const asyncInc = async x => x + 1;
const asyncDouble = async x => x * 2;
const incrementAndDouble = asyncPipe(asyncInc, asyncDouble);
incrementAndDouble(20).then(console.log); // 42Schreiben Sie eine Funktion asyncPipe , die mehrere Promise-zurückgebende Funktionen zusammensetzt. Sie nimmt eine Liste von Promise-zurückgebenden Funktionen entgegen und gibt eine neue Funktion zurück, die diese der Reihe nach anwendet. Wenn Sie aufrufen incrementAndDouble(20), es wird zuerst erhöht 20 auf 21 und dann wird es verdoppelt auf 42.
Aber diese Version von asyncPipe funktioniert nur mit Funktionen, die Promises zurückgeben. Was ist, wenn Sie auch reguläre Funktionen mit asynchronen Funktionen kombinieren möchten?
Man kann asyncPipe mit async / awaitschreiben.
const asyncPipe = (...fns) => x => fns.reduce(async (y, f) => f(await y), x);
const asyncInc = async x => x + 1;
const double = x => x * 2;
const asyncSquare = async x => x * x;
const incrementAndDouble = asyncPipe(asyncInc, double, asyncSquare);
incrementAndDouble(20).then(console.log); // 1764 Schauen wir uns die Schritte in asyncPipe.
Sie fragen sich vielleicht, warum Sie await y anstelle von await f(y) in der Reducer-Funktion innerhalb von asyncPipeverwenden. Wenn Sie asyncPipe umstrukturieren, um await f(y)zu verwenden, funktioniert es nicht und das obige Codebeispiel gibt NaNzurück.
Der Grund dafür ist, dass aufgrund von async der Akkumulator y immer ein ausstehendes Promise zurückgibt, daher müssen Sie es awaiten. Wenn Sie es nicht awaiten, funktioniert der erste Schritt, weil er zurückgeben würde await (asyncInc(20)), was 21. Aber aufgrund des async -Schlüsselworts in der Reducer-Funktion wäre der Akkumulator ein ausstehendes Promise von 21. Im zweiten Schritt würden Sie dann await double(Promise.resolve(21))aufrufen, was NaN ist, weil Promise.resolve(21) * 2 versuchen würde, ein Objekt – das Promise – mit zwei zu multiplizieren.
Das Komponieren von Promises ermöglicht es Ihnen, asynchrone Operationen auf saubere und wartbare Weise zu verketten und dabei alle Vorteile der funktionalen Programmierung zu nutzen.
<ReadNext posts={[ { ...unleashJavaScriptsPotential, slug: 'unleash-javascripts-potential-with-functional-programming' }, { ...javascriptGeneratorsExplainedSeniorLevel, slug: 'javascript-generators-explained-senior-level' }, ]} />