Suche…
Einführung
async
und await
bauen auf Versprechungen und Generatoren auf, um asynchrone Aktionen inline auszudrücken. Dies macht die Pflege von asynchronem Code oder Callback-Code wesentlich einfacher.
Funktionen mit dem async
Schlüsselwort geben ein Promise
und können mit dieser Syntax aufgerufen werden.
Innerhalb einer async function
das await
Schlüsselwort auf ein beliebiges Promise
angewendet werden und bewirkt, dass der gesamte Funktionsrumpf nach dem await
ausgeführt wird, nachdem das Versprechen aufgelöst wurde.
Syntax
- Async-Funktion foo () {
...
Erwarte asyncCall ()
} - async-Funktion () {...}
- async () => {...}
- (async () => {
const data = asyncCall () erwarten
console.log (data)}) ()
Bemerkungen
Async-Funktionen sind ein syntaktischer Zucker über Versprechungen und Generatoren. Sie helfen Ihnen dabei, Ihren Code lesbarer, wartbarer zu machen, Fehler leichter zu erkennen und weniger Einzug zu haben.
Einführung
Eine Funktion wie definiert async
ist eine Funktion , die asynchrone Aktionen ausführen können , aber immer noch synchron aussehen. Auf diese Weise wird die Funktion mit dem Schlüsselwort await
, während sie darauf wartet, dass ein Versprechen aufgelöst oder abgelehnt wird.
Hinweis: Async-Funktionen sind ein Vorschlag der Stufe 4 ("Abgeschlossen"), der auf dem richtigen Weg in den ECMAScript-Standard 2017 aufgenommen werden soll.
Verwenden Sie beispielsweise die promisebasierte Fetch-API :
async function getJSON(url) {
try {
const response = await fetch(url);
return await response.json();
}
catch (err) {
// Rejections in the promise will get thrown here
console.error(err.message);
}
}
Eine asynchrone Funktion gibt immer ein Promise selbst zurück, sodass Sie es in anderen asynchronen Funktionen verwenden können.
Pfeil funktion stil
const getJSON = async url => {
const response = await fetch(url);
return await response.json();
}
Weniger Einzug
Mit Versprechen:
function doTheThing() {
return doOneThing()
.then(doAnother)
.then(doSomeMore)
.catch(handleErrors)
}
Bei asynchronen Funktionen:
async function doTheThing() {
try {
const one = await doOneThing();
const another = await doAnother(one);
return await doSomeMore(another);
} catch (err) {
handleErrors(err);
}
}
Beachten Sie, wie sich die Rückgabe unten und nicht oben befindet, und Sie verwenden die systemeigenen Mechanismen zur Fehlerbehandlung ( try/catch
) der Sprache.
Warten Sie und Vorrang des Bedieners
Sie müssen die Operator-Vorrangigkeit berücksichtigen, wenn Sie das await
Schlüsselwort verwenden.
Stellen Sie sich vor, wir haben eine asynchrone Funktion, die eine weitere asynchrone Funktion getUnicorn()
, getUnicorn()
die eine Promise zurückgibt, die in eine Instanz der Klasse Unicorn
. Nun wollen wir die Größe des Einhorns mit der getSize()
Methode dieser Klasse ermitteln.
Schauen Sie sich den folgenden Code an:
async function myAsyncFunction() {
await getUnicorn().getSize();
}
Auf den ersten Blick scheint es gültig zu sein, ist es aber nicht. Aufgrund der Bedienerpriorität entspricht dies dem Folgenden:
async function myAsyncFunction() {
await (getUnicorn().getSize());
}
Hier versuchen wir, die Methode getSize()
des Promise-Objekts getSize()
, was wir nicht wollen.
Stattdessen sollten wir Klammern verwenden, um getSize()
dass wir zuerst auf das Einhorn warten möchten, und dann die Methode getSize()
des Ergebnisses aufrufen:
async function asyncFunction() {
(await getUnicorn()).getSize();
}
Na sicher. Die vorherige Version könnte in einigen Fällen gültig sein, z. B. wenn die getUnicorn()
Funktion synchron war, die getSize()
Methode jedoch asynchron war.
Async-Funktionen im Vergleich zu Versprechen
async
ersetzen nicht den Promise
Typ. Sie fügen Sprachschlüsselwörter hinzu, die das Aufrufen von Versprechen erleichtern. Sie sind austauschbar:
async function doAsyncThing() { ... }
function doPromiseThing(input) { return new Promise((r, x) => ...); }
// Call with promise syntax
doAsyncThing()
.then(a => doPromiseThing(a))
.then(b => ...)
.catch(ex => ...);
// Call with await syntax
try {
const a = await doAsyncThing();
const b = await doPromiseThing(a);
...
}
catch(ex) { ... }
Jede Funktion, die Ketten von Versprechen verwendet, kann mit await
neu geschrieben werden:
function newUnicorn() {
return fetch('unicorn.json') // fetch unicorn.json from server
.then(responseCurrent => responseCurrent.json()) // parse the response as JSON
.then(unicorn =>
fetch('new/unicorn', { // send a request to 'new/unicorn'
method: 'post', // using the POST method
body: JSON.stringify({unicorn}) // pass the unicorn to the request body
})
)
.then(responseNew => responseNew.json())
.then(json => json.success) // return success property of response
.catch(err => console.log('Error creating unicorn:', err));
}
Die Funktion kann mit async
/ await
wie folgt async
werden:
async function newUnicorn() {
try {
const responseCurrent = await fetch('unicorn.json'); // fetch unicorn.json from server
const unicorn = await responseCurrent.json(); // parse the response as JSON
const responseNew = await fetch('new/unicorn', { // send a request to 'new/unicorn'
method: 'post', // using the POST method
body: JSON.stringify({unicorn}) // pass the unicorn to the request body
});
const json = await responseNew.json();
return json.success // return success property of response
} catch (err) {
console.log('Error creating unicorn:', err);
}
}
Diese async
Variante von newUnicorn()
scheint ein Promise
, aber tatsächlich gab es mehrere await
Schlüsselwörter. Jeder gab ein Promise
, also hatten wir wirklich eine Sammlung von Versprechungen und keine Kette.
In der Tat können wir es als function*
vorstellen, wobei jedes await
ein yield new Promise
. Die Ergebnisse jedes Versprechens werden jedoch von den nächsten benötigt, um die Funktion fortzusetzen. Aus diesem Grund wird das zusätzliche Schlüsselwort async
für die Funktion benötigt (sowie das await
Schlüsselwort beim Aufruf der Versprechungen), da Javascript dazu aufgefordert wird, automatisch einen Beobachter für diese Iteration zu erstellen. Die von async function newUnicorn()
Promise
async function newUnicorn()
wenn diese Iteration abgeschlossen ist.
In der Praxis müssen Sie das nicht berücksichtigen. await
verbirgt das Versprechen, und async
verbirgt die Iteration des Generators.
Sie können async
Funktionen aufrufen, als wären sie Versprechungen, und await
ein Versprechen oder eine async
Funktion await
. Sie müssen nicht await
eine asynchrone Funktion await
, genauso wie Sie ein Versprechen ohne .then()
ausführen können.
Sie können auch eine Verwendung async
IIFE , wenn Sie sofort , dass Code ausgeführt werden sollen:
(async () => {
await makeCoffee()
console.log('coffee is ready!')
})()
Schleifen mit Async erwarten
Wenn Sie in Schleifen async erwarten, können einige dieser Probleme auftreten.
Wenn Sie nur versuchen , im Inneren warten zu verwenden forEach
, wird dies einen werfen Unexpected token
Fehler.
(async() => {
data = [1, 2, 3, 4, 5];
data.forEach(e => {
const i = await somePromiseFn(e);
console.log(i);
});
})();
Dies ist darauf zurückzuführen, dass Sie die Pfeilfunktion irrtümlicherweise als Block gesehen haben. Das await
erfolgt im Kontext der Rückruffunktion, die nicht async
.
Der Interpreter schützt uns davor, den obigen Fehler zu machen, aber wenn Sie den forEach
Rückruf async
hinzufügen, werden keine Fehler ausgegeben. Sie denken vielleicht, dass dies das Problem löst, aber es funktioniert nicht wie erwartet.
Beispiel:
(async() => {
data = [1, 2, 3, 4, 5];
data.forEach(async(e) => {
const i = await somePromiseFn(e);
console.log(i);
});
console.log('this will print first');
})();
Dies geschieht, weil die asynchrone Callback-Funktion nur sich selbst anhalten kann und nicht die übergeordnete async-Funktion.
Sie könnten eine asyncForEach-Funktion schreiben, die ein Versprechen zurückgibt, und dann könnten Sie so etwas wie
await asyncForEach(async (e) => await somePromiseFn(e), data )
. Es gibt jedoch bessere Möglichkeiten, dies zu tun, und zwar einfach eine Schleife zu verwenden.
Sie können eine for-of
Schleife oder eine for/while
Schleife verwenden. Dabei ist es egal, welche Sie auswählen.
(async() => {
data = [1, 2, 3, 4, 5];
for (let e of data) {
const i = await somePromiseFn(e);
console.log(i);
}
console.log('this will print last');
})();
Aber es gibt noch einen anderen Haken. Diese Lösung wartet, bis der Aufruf von somePromiseFn
abgeschlossen ist, bevor der nächste somePromiseFn
.
Dies ist großartig, wenn Sie möchten, dass die somePromiseFn
der somePromiseFn
nach ausgeführt werden. Wenn Sie jedoch gleichzeitig ausführen möchten, müssen Sie auf Promise.all
await
.
(async() => {
data = [1, 2, 3, 4, 5];
const p = await Promise.all(data.map(async(e) => await somePromiseFn(e)));
console.log(...p);
})();
Promise.all
erhält eine Reihe von Versprechen als einzigen Parameter und gibt ein Versprechen zurück. Wenn alle Versprechen im Array gelöst sind, wird auch das zurückgegebene Versprechen gelöst. Wir await
dieses Versprechen und wenn es gelöst ist, stehen alle unsere Werte zur Verfügung.
Die obigen Beispiele sind voll lauffähig. Die somePromiseFn
Funktion kann als somePromiseFn
mit Timeout ausgeführt werden. Sie können die Beispiele in babel-repl mit mindestens dem stage-3
Preset ausprobieren und sich die Ausgabe ansehen.
function somePromiseFn(n) {
return new Promise((res, rej) => {
setTimeout(() => res(n), 250);
});
}
Gleichzeitige asynchrone (parallele) Operationen
Oft möchten Sie asynchrone Operationen parallel ausführen. Es gibt eine direkte Syntax, die dies im Vorschlag für async
/ await
unterstützt. Da await
jedoch auf ein Versprechen wartet, können Sie mehrere Zusagen in Promise.all
, um Promise.all
zu warten:
// Not in parallel
async function getFriendPosts(user) {
friendIds = await db.get("friends", {user}, {id: 1});
friendPosts = [];
for (let id in friendIds) {
friendPosts = friendPosts.concat( await db.get("posts", {user: id}) );
}
// etc.
}
Dies führt jede Abfrage aus, um die Beiträge jedes Freundes seriell abzurufen, sie können jedoch gleichzeitig durchgeführt werden:
// In parallel
async function getFriendPosts(user) {
friendIds = await.db.get("friends", {user}, {id: 1});
friendPosts = await Promise.all( friendIds.map(id =>
db.get("posts", {user: id})
);
// etc.
}
Dadurch wird die Liste der IDs durchlaufen, um eine Reihe von Versprechen zu erstellen. await
bis alle Versprechen vollständig sind. Promise.all
kombiniert sie zu einem einzigen Versprechen, aber sie werden parallel ausgeführt.