// js · Web Platform Advent #3
async/await in JavaScript: ein praktischer Leitfaden
Wie async und await funktionieren, wie sie mit Promises zusammenhängen, Fehlerbehandlung mit try/catch, await in Schleifen und der Fallstrick seriell vs. parallel — mit ausführbaren Beispielen.
Die Schlüsselwörter async und await sind Syntax über Promises. Sie erlauben es, asynchronen Code zu schreiben, der sich von oben nach unten wie synchroner Code liest, dabei aber im Hintergrund nicht blockierend bleibt. Wenn Sie Promises kennen, kennen Sie bereits async/await — es nimmt nur die Zeremonie von .then() weg.
Die Grundlagen
Markieren Sie eine Funktion als async, und Sie können darin await verwenden. await pausiert diese Funktion, bis das Promise entschieden ist, und gibt Ihnen dann den aufgelösten Wert:
async function getUser(id) {
const res = await fetch('/api/user/' + id);
const user = await res.json();
return user.name;
}
getUser(42).then((name) => console.log(name)); Zwei Dinge zum Merken: eine async-Funktion gibt immer ein Promise zurück (was immer Sie zurückgeben, wird in eines verpackt), und await funktioniert nur innerhalb einer async-Funktion — außer auf der obersten Ebene eines ES-Moduls, wo Top-Level-await erlaubt ist.
async/await vs. Promises
Es ist dieselbe Maschinerie. Diese Promise-Kette …
fetch('/api/user')
.then((res) => res.json())
.then((user) => console.log(user.name)); … ist genau gleichwertig mit dieser async-Version, die die meisten Leute leichter zu lesen und zu debuggen finden:
const res = await fetch('/api/user');
const user = await res.json();
console.log(user.name); Sie können sie frei mischen — await jedes Promise, einschließlich des Ergebnisses von Promise.all().
Fehlerbehandlung mit try/catch
Mit async/await fangen Sie abgelehnte Promises mit gewöhnlichem try/catch ab, demselben Konstrukt, das Sie für synchrone Fehler verwenden:
async function loadUser(id) {
try {
const res = await fetch('/api/user/' + id);
if (!res.ok) throw new Error('HTTP ' + res.status);
return await res.json();
} catch (err) {
console.error('Could not load user:', err.message);
return null;
} finally {
console.log('loadUser finished');
}
} Ein abgelehntes await wirft an dieser Zeile eine Ausnahme, sodass der catch-Block läuft. Der finally-Block läuft, ob ein Fehler aufgetreten ist oder nicht.
Der große Fallstrick: serielles vs. paralleles await
Der häufigste async/await-Fehler ist, unabhängige Aufgaben eine nach der anderen abzuwarten. Das läuft seriell — die zweite Anfrage startet nicht, bevor die erste fertig ist:
// Slow: total time = a + b + c
const a = await fetchA();
const b = await fetchB();
const c = await fetchC(); Wenn die Aufgaben nicht voneinander abhängen, starten Sie sie alle zuerst und warten dann gemeinsam mit Promise.all ab. Jetzt laufen sie parallel:
// Fast: total time ≈ the slowest one
const [a, b, c] = await Promise.all([fetchA(), fetchB(), fetchC()]); await in Schleifen
Eine einfache for-Schleife mit await verarbeitet Elemente eines nach dem anderen, was korrekt ist, wenn jeder Schritt vom vorherigen abhängt oder Sie einen Server nicht überlasten dürfen:
for (const id of ids) {
const item = await fetchItem(id); // sequential — one after another
console.log(item);
} Um sie nebenläufig zu verarbeiten, bilden Sie ein Array von Promises ab und nutzen await Promise.all. Beachten Sie, dass forEach nicht auf async-Callbacks wartet — verwenden Sie stattdessen map + Promise.all:
const items = await Promise.all(ids.map((id) => fetchItem(id))); Schnellreferenz
| Ziel | Muster |
|---|---|
| Auf ein Promise warten | const x = await p; |
| Fehler behandeln | try { await ... } catch (e) { ... } |
| Unabhängige Aufgaben parallel ausführen | await Promise.all([...]) |
| Sequentielle Schleife | for...of mit await |
| Nebenläufige Schleife | map → Promise.all |
async/await ersetzt Promises nicht — es macht sie lesbar. Behalten Sie Promise.all für Parallelität in Ihrem Werkzeugkasten, greifen Sie für Fehler zu try/catch und achten Sie auf das versehentliche serielle await, dann haben Sie asynchrones JavaScript gut im Griff.