Mit Async-Funktionen können Sie Promise-basierten Code so schreiben, als wäre er synchron.
In Chrome, Edge, Firefox und Safari sind asynchrone Funktionen standardmäßig aktiviert. sind sie wirklich fantastisch. Sie ermöglichen es Ihnen, Promise-basierten Code wenn sie synchron wären, aber ohne den Hauptthread zu blockieren. Sie sorgen dafür, asynchroner Code, weniger „clever“ und besser lesbar sind.
So funktionieren asynchrone Funktionen:
async function myFirstAsyncFunction() {
try {
const fulfilledValue = await promise;
} catch (rejectedValue) {
// …
}
}
Wenn Sie das Schlüsselwort async
vor einer Funktionsdefinition verwenden, können Sie
await
innerhalb der Funktion. Wenn du ein Promise mit await
aktivierst, wird die Funktion pausiert
nicht blockieren,
bis sich das Versprechen abwickelt. Wenn das Versprechen erfüllt ist,
um den Wert zurückzubekommen. Wenn das Versprechen abgelehnt wird, wird der abgelehnte Wert ausgegeben.
Unterstützte Browser
Unterstützte Browser
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
Beispiel: Protokollieren eines Abrufs
Angenommen, Sie möchten eine URL abrufen und die Antwort als Text protokollieren. So sieht es aus: Promise verwenden:
function logFetch(url) {
return fetch(url)
.then((response) => response.text())
.then((text) => {
console.log(text);
})
.catch((err) => {
console.error('fetch failed', err);
});
}
Bei Verwendung asynchroner Funktionen ist das Gleiche dasselbe:
async function logFetch(url) {
try {
const response = await fetch(url);
console.log(await response.text());
} catch (err) {
console.log('fetch failed', err);
}
}
Die Anzahl der Zeilen ist identisch, aber alle Callbacks sind verschwunden. So können Sie leichter zu lesen, vor allem für diejenigen, die weniger mit Versprechen vertraut sind.
Asynchrone Rückgabewerte
Asynchrone Funktionen geben immer ein Promise zurück, unabhängig davon, ob du await
verwendest oder nicht. Das
Versprechen mit dem, was die asynchrone Funktion zurückgibt, oder lehnt es mit
was die asynchrone Funktion auslöst. Also bei:
// wait ms milliseconds
function wait(ms) {
return new Promise((r) => setTimeout(r, ms));
}
async function hello() {
await wait(500);
return 'world';
}
...Der Aufruf von hello()
gibt ein Promise zurück, das mit "world"
erfüllt wird.
async function foo() {
await wait(500);
throw Error('bar');
}
...Der Aufruf von foo()
gibt ein Promise zurück, das mit Error('bar')
ablehnt.
Beispiel: Antwort streamen
Der Vorteil asynchroner Funktionen steigt in komplexeren Beispielen. Sag, du wolltest um eine Antwort zu streamen, während die Blöcke abgemeldet werden, und gibt die endgültige Größe zurück.
Hier ist es mit Versprechen:
function getResponseSize(url) {
return fetch(url).then((response) => {
const reader = response.body.getReader();
let total = 0;
return reader.read().then(function processResult(result) {
if (result.done) return total;
const value = result.value;
total += value.length;
console.log('Received chunk', value);
return reader.read().then(processResult);
});
});
}
Jake „wielder of Promis“ (wie viel Versprechen) Archibald. Herausfinden, wie ich anrufe
processResult()
in sich selbst, um eine asynchrone Schleife einzurichten? Durch das Schreiben
fühle ich mich sehr schlau. Aber wie die meisten „smarten“ muss man ihn erst ansehen,
um herauszufinden, was es tut, wie z. B. eines dieser magischen Bilder
aus den 90ern.
Versuchen wir das noch einmal mit asynchronen Funktionen:
async function getResponseSize(url) {
const response = await fetch(url);
const reader = response.body.getReader();
let result = await reader.read();
let total = 0;
while (!result.done) {
const value = result.value;
total += value.length;
console.log('Received chunk', value);
// get the next result
result = await reader.read();
}
return total;
}
Die cleveren ist verschwunden. Die asynchrone Schleife, die mich so selbstbewusst gefühlt hat,
durch eine vertrauenswürdige, langweilige Während-Schleife ersetzt. Viel besser. Zukünftig erhalten Sie
asynchrone Iterationen
Das würde
ersetze die while
-Schleife durch eine For-of-Schleife, um sie noch übersichtlicher zu gestalten.
Syntax anderer asynchroner Funktionen
Ich habe Ihnen bereits async function() {}
gezeigt, aber das Keyword async
kann
zusammen mit einer anderen Funktionssyntax verwendet:
Pfeilfunktionen
// map some URLs to json-promises
const jsonPromises = urls.map(async (url) => {
const response = await fetch(url);
return response.json();
});
Objektmethoden
const storage = {
async getAvatar(name) {
const cache = await caches.open('avatars');
return cache.match(`/avatars/${name}.jpg`);
}
};
storage.getAvatar('jaffathecake').then(…);
Klassenmethoden
class Storage {
constructor() {
this.cachePromise = caches.open('avatars');
}
async getAvatar(name) {
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
const storage = new Storage();
storage.getAvatar('jaffathecake').then(…);
Vorsicht! Vermeiden Sie die Verwendung von sequenziellen Anzeigen.
Obwohl Sie Code schreiben, der synchron aussieht, stellen Sie sicher, dass Sie den Code die Möglichkeit, Dinge parallel zu tun.
async function series() {
await wait(500); // Wait 500ms…
await wait(500); // …then wait another 500ms.
return 'done!';
}
Der obige Vorgang dauert 1.000 ms, während:
async function parallel() {
const wait1 = wait(500); // Start a 500ms timer asynchronously…
const wait2 = wait(500); // …meaning this timer happens in parallel.
await Promise.all([wait1, wait2]); // Wait for both timers in parallel.
return 'done!';
}
Der obige Vorgang dauert 500 ms, da beide Wartezeiten gleichzeitig erfolgen. Sehen wir uns ein Beispiel aus der Praxis an.
Beispiel: Abrufe der Reihe nach ausgeben
Angenommen, Sie möchten eine Reihe von URLs abrufen und diese so schnell wie möglich in der in der richtigen Reihenfolge.
Tief durchatmen – so sieht das mit Versprechen aus:
function markHandled(promise) {
promise.catch(() => {});
return promise;
}
function logInOrder(urls) {
// fetch all the URLs
const textPromises = urls.map((url) => {
return markHandled(fetch(url).then((response) => response.text()));
});
// log them in order
return textPromises.reduce((chain, textPromise) => {
return chain.then(() => textPromise).then((text) => console.log(text));
}, Promise.resolve());
}
Ja, das stimmt, ich verwende reduce
, um eine Reihe von Versprechen zu verketten. Ich bin
intelligent. Aber diese Programmierung ist so intelligent, dass Sie ohne sie besser auskommen würden.
Bei der Konvertierung der obigen Anweisungen in eine asynchrone Funktion ist es jedoch verlockend, zu sequenziell:
async function logInOrder(urls) { for (const url of urls) { const response = await fetch(url); console.log(await response.text()); } }
function markHandled(...promises) { Promise.allSettled(promises); } async function logInOrder(urls) { // fetch all the URLs in parallel const textPromises = urls.map(async (url) => { const response = await fetch(url); return response.text(); }); markHandled(...textPromises); // log them in sequence for (const textPromise of textPromises) { console.log(await textPromise); } }
Behelfslösung für Browserunterstützung: Generatoren
Wenn Sie Ihre Anzeigen auf Browser ausrichten, die Generatoren unterstützen (einschließlich der aktuellen Version jedes gängigen Browsers ) können Sie asynchrone Polyfill-Funktionen verwenden.
Babel übernimmt das für Sie. ein Beispiel aus der Babel-REPL
- wie ähnlich der transpilierte Code ist. Diese Transformation ist Teil Die Voreinstellung es2017 in Babel.
Ich empfehle den Transpiler-Ansatz, da Sie ihn einfach deaktivieren können, sobald Sie Zielbrowser unterstützen asynchrone Funktionen. Wenn Sie jedoch wirklich keine können Sie Polyfill von Babel und verwenden Sie es selbst. Anstelle von:
async function slowEcho(val) {
await wait(1000);
return val;
}
...brauchst du den Polyfill und schreibe:
const slowEcho = createAsyncFunction(function* (val) {
yield wait(1000);
return val;
});
Du musst einen Generator (function*
) an createAsyncFunction
übergeben.
und verwende yield
statt await
. Ansonsten funktioniert das Ganze gleich.
Problemumgehung: Regenerator
Bei älteren Browsern kann Babel auch Generatoren transpilieren, Sie können asynchrone Funktionen bis hin zu IE8 verwenden. Dazu benötigen Sie Babel-Voreinstellung es2017 und der Voreinstellung „es2015“.
Die Ausgabe ist nicht so hübsch, also achten Sie auf aufgeblähter Code.
Alles asynchron!
Sobald asynchrone Funktionen in allen Browsern verfügbar sind, sollten Sie sie auf jedem versprochene Funktion! Sie machen Ihren Code nicht nur ordentlicher, sondern kannst du dafür sorgen, dass diese Funktion immer ein Versprechen zurückgibt.
Ich war schon sehr gespannt auf asynchrone Funktionen 2014 und ist es toll, wenn sie im echten Leben im Browser landen. Hoppla!