async function
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since April 2017.
Die async function
-Deklaration erstellt eine Bindung einer neuen asynchronen Funktion mit einem gegebenen Namen. Das await
-Schlüsselwort ist im Funktionskörper erlaubt und ermöglicht eine asynchrone, auf Promises basierende Verhaltensweise, die in einem klareren Stil geschrieben werden kann, ohne dass explizit Promise-Ketten konfiguriert werden müssen.
Sie können asynchrone Funktionen auch mit dem async function
Ausdruck definieren.
Probieren Sie es aus
Syntax
async function name(param0) {
statements
}
async function name(param0, param1) {
statements
}
async function name(param0, param1, /* …, */ paramN) {
statements
}
Hinweis: Zwischen async
und function
darf kein Zeilenumbruch stehen, da andernfalls ein Semikolon automatisch eingefügt wird, wodurch async
zu einem Bezeichner und der Rest zu einer function
-Deklaration wird.
Parameter
name
-
Der Name der Funktion.
param
Optional-
Der Name eines formalen Parameters der Funktion. Für die Syntax der Parameter, siehe die Funktionsreferenz.
statements
Optional-
Die Anweisungen, die den Körper der Funktion bilden. Der
await
-Mechanismus kann verwendet werden.
Beschreibung
Eine async function
-Deklaration erstellt ein AsyncFunction
-Objekt. Jedes Mal, wenn eine asynchrone Funktion aufgerufen wird, gibt sie ein neues Promise
zurück, das mit dem von der asynchronen Funktion zurückgegebenen Wert aufgelöst oder mit einer innerhalb der asynchronen Funktion nicht abgefangenen Ausnahme abgelehnt wird.
Asynchrone Funktionen können null oder mehr await
Ausdrücke enthalten. Await-Ausdrücke lassen Funktionen, die ein Promise zurückgeben, so erscheinen, als wären sie synchron, indem sie die Ausführung aussetzen, bis das zurückgegebene Promise erfüllt oder abgelehnt wird. Der aufgelöste Wert des Promises wird als der Rückgabewert des Await-Ausdrucks behandelt. Der Gebrauch von async
und await
ermöglicht die Verwendung von normalen try
/catch
-Blöcken um asynchronen Code.
Hinweis: Das await
-Schlüsselwort ist nur innerhalb von asynchronen Funktionen im regulären JavaScript-Code gültig. Wenn Sie es außerhalb des Körpers einer asynchronen Funktion verwenden, erhalten Sie einen SyntaxError
.
await
kann eigenständig mit JavaScript-Modulen. verwendet werden.
Hinweis: Der Zweck von async
/await
ist es, die benötigte Syntax zu vereinfachen,
um auf Promise-basierte APIs zuzugreifen. Das Verhalten von async
/await
ähnelt der Kombination von Generatoren und Promises.
Asynchrone Funktionen geben immer ein Promise zurück. Wenn der Rückgabewert einer asynchronen Funktion nicht explizit ein Promise ist, wird er implizit in ein Promise gewrappt.
Betrachten Sie zum Beispiel den folgenden Code:
async function foo() {
return 1;
}
Er ist ähnlich wie:
function foo() {
return Promise.resolve(1);
}
Beachten Sie, dass der Rückgabewert einer asynchronen Funktion zwar so behandelt wird, als wäre er in einem Promise.resolve
gewrappt, sie jedoch nicht identisch sind. Eine asynchrone Funktion gibt eine andere Referenz zurück, während Promise.resolve
dieselbe Referenz zurückgibt, wenn der gegebene Wert ein Promise ist. Dies kann problematisch sein, wenn Sie die Gleichheit eines Promises und eines Rückgabewerts einer asynchronen Funktion überprüfen möchten.
const p = new Promise((res, rej) => {
res(1);
});
async function asyncReturn() {
return p;
}
function basicReturn() {
return Promise.resolve(p);
}
console.log(p === basicReturn()); // true
console.log(p === asyncReturn()); // false
Der Körper einer asynchronen Funktion kann als in Null oder mehr await-Ausdrücken geteilt gedacht werden. Der Top-Level-Code, bis einschließlich des ersten await-Ausdrucks (falls vorhanden), wird synchron ausgeführt. Auf diese Weise wird eine asynchrone Funktion ohne einen await-Ausdruck synchron ausgeführt. Wenn sich jedoch ein await-Ausdruck im Funktionskörper befindet, wird die asynchrone Funktion immer asynchron abgeschlossen.
Zum Beispiel:
async function foo() {
await 1;
}
Es ist auch gleichwertig zu:
function foo() {
return Promise.resolve(1).then(() => undefined);
}
Der Code nach jedem await-Ausdruck kann als in einer .then
-Rückruffunktion existierend betrachtet werden. Auf diese Weise wird mit jedem erneuten Eintritt in die Funktion schrittweise eine Promise-Kette aufgebaut. Der Rückgabewert bildet das letzte Glied in der Kette.
Im folgenden Beispiel warten wir nacheinander auf zwei Promises. Der Fortschritt führt durch die Funktion foo
in drei Phasen.
-
Die erste Zeile des Körpers der Funktion
foo
wird synchron ausgeführt, wobei der await-Ausdruck mit dem ausstehenden Promise konfiguriert ist. Der Fortschritt durchfoo
wird dann ausgesetzt und die Kontrolle wird an die Funktion zurückgegeben, diefoo
aufgerufen hat. -
Einige Zeit später, wenn das erste Promise entweder erfüllt oder abgelehnt wurde,
kehrt die Kontrolle zurück zu
foo
. Das Ergebnis der Erfüllung des ersten Promises (falls es nicht abgelehnt wurde) wird aus dem await-Ausdruck zurückgegeben. Hier wird1
zugewiesen anresult1
. Der Fortschritt geht weiter, und der zweite await-Ausdruck wird ausgewertet. Wiederum wird der Fortschritt durchfoo
ausgesetzt und die Kontrolle wird zurückgegeben. -
Einige Zeit später, wenn das zweite Promise entweder erfüllt oder abgelehnt wird,
tritt die Kontrolle wieder in
foo
ein. Das Ergebnis der Auflösung des zweiten Promises wird aus dem zweiten await-Ausdruck zurückgegeben. Hier wird2
zugewiesen anresult2
. Die Kontrolle wechselt zum Rückgabewert (falls vorhanden). Der Standard- rückgabewert vonundefined
wird als Auflösungswert des aktuellen Promises zurückgegeben.
async function foo() {
const result1 = await new Promise((resolve) =>
setTimeout(() => resolve("1")),
);
const result2 = await new Promise((resolve) =>
setTimeout(() => resolve("2")),
);
}
foo();
Beachten Sie, wie die Promise-Kette nicht in einem Zug aufgebaut wird. Stattdessen wird die Promise-Kette in Phasen aufgebaut, da die Kontrolle sukzessive aus der asynchronen Funktion herausverlagert und zurückgegeben wird. Daher müssen wir uns des Fehlerbehandlungsverhaltens bewusst sein, wenn wir mit gleichzeitigen asynchronen Operationen umgehen.
Zum Beispiel wird im folgenden Code ein nicht behandelter Promise-Ablehnungsfehler geworfen,
selbst wenn ein .catch
-Handler weiter entlang der Promise-Kette konfiguriert wurde. Dies liegt daran, dass p2
nicht in die Promise-Kette "verdrahtet" wird, bis die Kontrolle von p1
zurückkehrt.
async function foo() {
const p1 = new Promise((resolve) => setTimeout(() => resolve("1"), 1000));
const p2 = new Promise((_, reject) => setTimeout(() => reject("2"), 500));
const results = [await p1, await p2]; // Do not do this! Use Promise.all or Promise.allSettled instead.
}
foo().catch(() => {}); // Attempt to swallow all errors...
async function
-Deklarationen verhalten sich ähnlich wie function
-Deklarationen – sie werden gehoben an den Anfang ihres Gültigkeitsbereichs und können überall in ihrem Gültigkeitsbereich aufgerufen werden, und können nur in bestimmten Kontexten neu deklariert werden.
Beispiele
Asynchrone Funktionen und Ausführungsreihenfolge
function resolveAfter2Seconds() {
console.log("starting slow promise");
return new Promise((resolve) => {
setTimeout(() => {
resolve("slow");
console.log("slow promise is done");
}, 2000);
});
}
function resolveAfter1Second() {
console.log("starting fast promise");
return new Promise((resolve) => {
setTimeout(() => {
resolve("fast");
console.log("fast promise is done");
}, 1000);
});
}
async function sequentialStart() {
console.log("== sequentialStart starts ==");
// 1. Start a timer, log after it's done
const slow = resolveAfter2Seconds();
console.log(await slow);
// 2. Start the next timer after waiting for the previous one
const fast = resolveAfter1Second();
console.log(await fast);
console.log("== sequentialStart done ==");
}
async function sequentialWait() {
console.log("== sequentialWait starts ==");
// 1. Start two timers without waiting for each other
const slow = resolveAfter2Seconds();
const fast = resolveAfter1Second();
// 2. Wait for the slow timer to complete, and then log the result
console.log(await slow);
// 3. Wait for the fast timer to complete, and then log the result
console.log(await fast);
console.log("== sequentialWait done ==");
}
async function concurrent1() {
console.log("== concurrent1 starts ==");
// 1. Start two timers concurrently and wait for both to complete
const results = await Promise.all([
resolveAfter2Seconds(),
resolveAfter1Second(),
]);
// 2. Log the results together
console.log(results[0]);
console.log(results[1]);
console.log("== concurrent1 done ==");
}
async function concurrent2() {
console.log("== concurrent2 starts ==");
// 1. Start two timers concurrently, log immediately after each one is done
await Promise.all([
(async () => console.log(await resolveAfter2Seconds()))(),
(async () => console.log(await resolveAfter1Second()))(),
]);
console.log("== concurrent2 done ==");
}
sequentialStart(); // after 2 seconds, logs "slow", then after 1 more second, "fast"
// wait above to finish
setTimeout(sequentialWait, 4000); // after 2 seconds, logs "slow" and then "fast"
// wait again
setTimeout(concurrent1, 7000); // same as sequentialWait
// wait again
setTimeout(concurrent2, 10000); // after 1 second, logs "fast", then after 1 more second, "slow"
await und Parallelität
In sequentialStart
wird die Ausführung 2 Sekunden für das erste
await
ausgesetzt, und dann noch eine Sekunde für das zweite await
. Der
zweite Timer wird nicht erstellt, bis der erste bereits ausgelöst wurde, sodass der Code nach 3 Sekunden beendet wird.
In sequentialWait
werden beide Timer erstellt und dann await
ed.
Die Timer laufen parallel, was bedeutet, dass der Code in 2 statt 3 Sekunden fertig ist,
d.h. nach dem langsamsten Timer.
Dennoch laufen die await
-Aufrufe immer noch in Serie, was bedeutet, dass der zweite
await
auf den ersten warten wird, bis er fertig ist. In diesem Fall wird das Ergebnis des
schnellsten Timers nach dem langsamsten verarbeitet.
Falls Sie nach zwei oder mehr gleichzeitig laufenden und abgeschlossenen Aufgaben sicher andere Aufgaben ausführen möchten, müssen Sie einen Aufruf von Promise.all()
oder Promise.allSettled()
abwarten, bevor diese Aufgabe ausgeführt wird.
Warnung: Die Funktionen sequentialWait
und concurrent1
sind nicht funktional gleichwertig.
In sequentialWait
, wenn das Promise fast
abgelehnt wird, bevor das Promise
slow
erfüllt ist, wird ein nicht behandelter Promise-Ablehnungsfehler ausgelöst,
unabhängig davon, ob der Aufrufer eine catch-Klausel konfiguriert hat.
In concurrent1
wird Promise.all
die Promise-Kette auf einmal verdrahten,
was bedeutet, dass der Vorgang bei einem Fehler schnell abgebrochen wird, unabhängig von der Reihenfolge der
Ablehnung der Promises, und der Fehler wird immer innerhalb der konfigurierten
Promise-Kette auftreten, wodurch er auf normale Weise abgefangen werden kann.
Umschreiben einer Promise-Kette mit einer asynchronen Funktion
Eine API, die ein Promise
zurückgibt, erzeugt eine Promise-Kette und
teilt die Funktion in viele Teile. Betrachten Sie den folgenden Code:
function getProcessedData(url) {
return downloadData(url) // returns a promise
.catch((e) => downloadFallbackData(url)) // returns a promise
.then((v) => processDataInWorker(v)); // returns a promise
}
er kann mit einer einzigen asynchronen Funktion wie folgt umgeschrieben werden:
async function getProcessedData(url) {
let v;
try {
v = await downloadData(url);
} catch (e) {
v = await downloadFallbackData(url);
}
return processDataInWorker(v);
}
Alternativ können Sie das Promise mit catch()
verketten:
async function getProcessedData(url) {
const v = await downloadData(url).catch((e) => downloadFallbackData(url));
return processDataInWorker(v);
}
In den beiden umgeschriebenen Versionen beachten Sie, dass es keine await
-Anweisung nach dem
return
-Schlüsselwort gibt, obwohl dies auch gültig wäre: Der Rückgabewert einer
asynchronen Funktion wird implizit in Promise.resolve
gewrappt - falls
es nicht bereits selbst ein Promise ist (wie in den Beispielen).
Spezifikationen
Specification |
---|
ECMAScript Language Specification # sec-async-function-definitions |
Browser-Kompatibilität
BCD tables only load in the browser