JavaScript async/await-Muster, die Sie kennen sollten
Asynchrone Programmierung ist ein Kernbestandteil des modernen JavaScript, und async/await hat sie deutlich lesbarer gemacht. Aber viele Entwickler kämpfen immer noch mit häufigen Fallstricken: unbehandelte Promise-Ablehnungen, langsame sequenzielle Anfragen oder verwirrende Fehlerbehandlung. In diesem Artikel erkunden wir praktische async/await-Muster, die Sie sofort anwenden können, um saubereren und effizienteren Code zu schreiben.
1. Grundlegende async/await-Syntax
Eine async-Funktion gibt immer ein Promise zurück. Das await-Schlüsselwort pausiert die Ausführung, bis das Promise sich auflöst, und gibt dann seinen aufgelösten Wert zurück. Wenn das Promise abgelehnt wird, wirft es einen Fehler, den Sie mit try/catch abfangen können.
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Fetch failed:', error);
}
}
Das ist die Grundlage. Aber reale Szenarien erfordern oft differenziertere Ansätze.
2. Fehler elegant behandeln
Einer der größten Vorteile von async/await ist, dass Sie try/catch sowohl für synchrone als auch für asynchrone Fehler verwenden können. Seien Sie jedoch vorsichtig mit mehreren await-Aufrufen in einem einzigen try-Block: Wenn der erste fehlschlägt, werden die nachfolgenden nicht ausgeführt.
Für eine bessere Granularität können Sie jeden await in ein eigenes try/catch einwickeln oder eine Hilfsfunktion verwenden, um wiederholte try/catch-Blöcke zu vermeiden.
async function getUser(id) {
try {
const user = await fetchUser(id);
return user;
} catch (error) {
console.error('Failed to fetch user:', error);
throw error;
}
}
3. Promises parallel ausführen
Das sequenzielle Awaiten mehrerer unabhängiger Promises ist ein häufiger Performance-Fehler. Wenn Sie Ergebnisse aus mehreren Quellen benötigen, verwenden Sie Promise.all(), um sie nebenläufig auszuführen.
// Sequenziell: langsam
const user = await fetchUser();
const posts = await fetchPosts();
// Parallel: schnell
const [user, posts] = await Promise.all([fetchUser(), fetchPosts()]);
Verwenden Sie Promise.allSettled(), wenn Sie warten möchten, bis alle Promises abgeschlossen sind, unabhängig davon, ob sie aufgelöst oder abgelehnt werden. Dies ist nützlich, wenn Sie Ergebnisse sammeln möchten, auch wenn einige fehlschlagen.
4. Sequenzielle Ausführung, wenn die Reihenfolge wichtig ist
Manchmal müssen Sie Elemente in der Reihenfolge verarbeiten, beispielsweise wenn jeder Schritt vom vorherigen abhängt. Verwenden Sie in diesem Fall eine for...of-Schleife mit await anstelle von forEach (das nicht auf asynchrone Callbacks wartet).
for (const item of items) {
await processItem(item);
}
Wenn Sie die Nebenläufigkeit begrenzen müssen, ziehen Sie Bibliotheken wie p-limit in Betracht oder implementieren Sie eine einfache Warteschlange.
5. Die async/await-Falle in Schleifen vermeiden
Die Verwendung von await innerhalb von Array.prototype.forEach ist ein klassischer Fehler: forEach wartet nicht auf den asynchronen Callback, sodass die Schleife beendet wird, bevor die Promises aufgelöst werden. Verwenden Sie immer for...of oder map mit Promise.all.
6. Async/await mit Timeouts
Um hängende Anfragen zu verhindern, können Sie ein Promise gegen ein Timeout antreten lassen.
function timeout(ms) {
return new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
);
}
await Promise.race([fetchData(), timeout(5000)]);
Dieses Muster ist praktisch für API-Aufrufe, die hängen bleiben könnten.
7. Async-Generatoren und for await...of
Async-Generatoren ermöglichen es Ihnen, einen Stream von Werten asynchron zu erzeugen. Sie können sie mit for await...of konsumieren.
async function* fetchPages() {
let page = 1;
while (true) {
const data = await fetch(`/api/items?page=${page}`);
if (data.length === 0) break;
yield data;
page++;
}
}
for await (const page of fetchPages()) {
console.log(page);
}
Dies ist großartig für paginierte APIs oder Streaming-Daten.
8. Häufige Fehler und wie man sie vermeidet
- Vergessen zu awaiten: Der Aufruf einer async-Funktion ohne await gibt ein Promise zurück, und Fehler bleiben möglicherweise unbehandelt.
- Verwendung von await in Nicht-async-Funktionen: Syntaxfehler. Markieren Sie die Funktion immer als async.
- Übermäßige Verwendung von sequenziellem Await: Verwenden Sie Promise.all, wenn möglich.
- Nichtbehandlung von Ablehnungen: Fügen Sie immer .catch oder try/catch hinzu.
9. Best Practices für sauberen async-Code
- Bevorzugen Sie async/await gegenüber rohen Promises aus Gründen der Lesbarkeit.
- Verwenden Sie Promise.all für unabhängige Operationen.
- Behandeln Sie Fehler auf der entsprechenden Ebene.
- Halten Sie async-Funktionen klein und fokussiert.
- Verwenden Sie async/await in Schleifen mit Vorsicht; wählen Sie das richtige Muster.
FAQ
Was ist der Unterschied zwischen async/await und Promises?
Async/await ist syntaktischer Zucker über Promises, der asynchronen Code synchron aussehen lässt. Es vereinfacht Verkettung und Fehlerbehandlung mit try/catch.
Kann ich await in einer forEach-Schleife verwenden?
Nein, forEach wartet nicht auf asynchrone Callbacks. Verwenden Sie stattdessen for...of oder Promise.all mit map.
Wie führe ich mehrere asynchrone Operationen parallel aus?
Verwenden Sie Promise.all(), um sie nebenläufig auszuführen und auf die Auflösung aller zu warten.
Wenn Sie mit JSON-APIs arbeiten, können Sie Antworten schnell mit unserem JSON Formatter formatieren und validieren.