async/await ist der meistgenutzte Weg, asynchronen Code in
JavaScript zu schreiben – und gleichzeitig der am häufigsten missverstandene.
Die Syntax sieht synchron aus, verhält sich aber asynchron. Wer das
Grundmodell nicht versteht, stolpert über Race Conditions, tote
Fehlerbehandlungen und unnötig langsame Abläufe. Dieses Tutorial räumt auf.
Das Grundmodell: die Event Loop
JavaScript ist single-threaded: Es gibt genau einen Call Stack, auf dem
Code ausgeführt wird. Zeitraubende Operationen (Netzwerk, Timer, Dateien)
delegiert die Laufzeitumgebung an die Umgebung – Browser oder Node.js –
und läuft weiter. Kommt das Ergebnis zurück, landet ein Callback in der
Message Queue und wird ausgeführt, sobald der Stack leer ist.
await pausiert also niemals das ganze Programm – es pausiert
nur die aktuelle async-Funktion und gibt die Kontrolle an die
Event Loop zurück. Alles andere läuft munter weiter.
Egal was eine async-Funktion zurückgibt – ein Wert, ein Promise,
ein geworfener Fehler – nach außen ist es immer ein Promise. Das macht
async-Funktionen komponierbar: Jede kann von jeder anderen
awaited werden.
Fehlerbehandlung: try/catch statt .catch()
Mit await wird aus asynchronem Fehlerhandling wieder ganz
normales try/catch. Aber Achtung: Der Fehler wird nur
gefangen, wenn das awaitinnerhalb des
try-Blocks steht.
javascript
// ✓ Richtig: await im try-Block
async function ladeProfil(id) {
try {
const antwort = await fetch(`/api/nutzer/${id}`);
if (!antwort.ok) {
throw new Error(`HTTP ${antwort.status}`);
}
return await antwort.json();
} catch (fehler) {
console.error('Laden fehlgeschlagen:', fehler);
return null;
}
}
// ✗ Falle: await außerhalb – Fehler geht verloren
async function kaputt() {
const promise = holeDaten(); // Fehler wird hier schon ausgelöst …
try {
const daten = await promise; // … und hier NICHT mehr gefangen
} catch (e) {
// kommt nie an → Unhandled Promise Rejection
}
}
Sequentiell vs. parallel: der häufigste Performance-Fehler
Wer mehrere awaits untereinander schreibt, zwingt sie in eine
Warteschlange – selbst wenn sie nichts voneinander wissen. Für unabhängige
Operationen gibt es Promise.all().
javascript
// Sequentiell: 3 × 200 ms = ~600 ms Gesamtzeit
const nutzer = await holeNutzer(1); // 200 ms
const bestellungen = await holeBestellungen(1); // 200 ms
const adresse = await holeAdresse(1); // 200 ms
// Parallel: alle drei starten sofort, ~200 ms Gesamtzeit
const [nutzer, bestellungen, adresse] = await Promise.all([
holeNutzer(1),
holeBestellungen(1),
holeAdresse(1),
]);
Achtung: Promise.all ist all-or-nothing
Lehnt ein einzelnes Promise ab, verwirft Promise.all()
alle Ergebnisse. Wenn du Teilergebnisse behalten willst, nutze
Promise.allSettled() – es liefert für jedes Promise
Status und Wert bzw. Fehler zurück.
Loops: await im forEach funktioniert nicht
Ein Klassiker: forEach kennt kein await – der
Callback wird synchron gestartet, die Schleife wartet nicht. Für
sequentielles Await nutze eine for…of-Schleife.
javascript
// ✗ Falle: forEach wartet nicht
ids.forEach(async (id) => {
await verarbeite(id); // läuft "los", aber die Schleife ist längst fertig
});
console.log('fertig'); // wird VOR der Verarbeitung ausgegeben
// ✓ Sequentiell mit for…of
for (const id of ids) {
await verarbeite(id);
}
console.log('fertig'); // wirklich erst nach allen Einträgen
// ✓ Parallel, wenn die Reihenfolge egal ist:
await Promise.all(ids.map(id => verarbeite(id)));
Top-Level Await
In ES-Modulen (Browser und Node.js ab 14.8) ist await auch
auf oberster Ebene erlaubt – praktisch für Konfigurationsdateien und
Startup-Logik:
async/await ist kein Ersatz für das Promise-Modell, sondern
syntaktischer Zucker darüber. Wer drei Regeln verinnerlicht, vermeidet
die meisten Fallen: await gehört in den try-Block,
unabhängige Operationen gehören in Promise.all() und
in Loops gehört await in for…of, nie in forEach.
PHP 8 hat den Alltag deutlich verändert: Enums ersetten Klassenkonstanten-Wüsten, readonly schützt Objekte vor versehentlicher Mutation und Match macht bedingte Logik lesbar. Ein Praxisüberblick mit Beispielen.
Wir nutzen Matomo für die Reichweitenmessung – im cookieless Verfahren:
Dabei werden keine Cookies gesetzt und keine Device-Fingerprints erstellt,
Ihre IP-Adresse wird sofort anonymisiert. Mehr dazu in unserer
Datenschutzerklärung.