Start · Sprachen · JavaScript · Kapitel · Asynchronität (Promises, async/await)
// Kapitel · JavaScript

Asynchronität (Promises, async/await)

Diese Sektion erklärt, wie JavaScript mit Promises und async/await asynchron arbeitet. Behandelt werden Verkettung, Fehlerbehandlung, parallele Ausführung und typische Fallstricke.

Kurzantwort
Asynchronität (Promises, async/await) in JavaScript — kurz erklärt

Asynchronität in JavaScript erlaubt es, langsame Operationen (Netzwerk, Dateien, Timer) auszuführen, ohne den Haupt-Thread zu blockieren. Grundbausteine: Callback-Funktionen, Promises und async/await. Der Event-Loop verarbeitet Micro-Tasks (Promise-Resolve) vor Macro-Tasks (setTimeout).

JavaScript läuft in einem einzigen Thread. Netzwerkzugriffe und Timer blockieren ihn trotzdem nicht, weil ihre Ergebnisse später über die Event Loop geliefert werden. Ein Promise steht für einen solchen zukünftigen Wert. async/await ist die gut lesbare Syntax, mit der du Promises verarbeitest.

Promises

Ein Promise hat einen von drei Zuständen: pending (ausstehend), fulfilled (erfüllt) oder rejected (abgelehnt). Mit then, catch und finally reagierst du auf das Ergebnis. Jeder dieser Aufrufe gibt ein neues Promise zurück, deshalb kannst du sie verketten.

const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

wait(500)
  .then(() => fetch('/api/user'))
  .then((res) => res.json())
  .catch((err) => console.error(err))
  .finally(() => console.log('fertig'));

async/await

Eine async-Funktion gibt immer ein Promise zurück. await hält die Funktion an, bis das Promise erfüllt ist, und liefert dann dessen Wert. Wird das Promise abgelehnt, wirft await eine Ausnahme, die du mit try/catch abfangen kannst.

const loadUser = async (id) => {
  try {
    const res = await fetch(`/api/users/${id}`);
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    const { name } = await res.json();
    return name;
  } catch (err) {
    console.error('Laden fehlgeschlagen:', err);
    throw err;
  }
};

Achtung: fetch lehnt bei HTTP-Fehlern wie 404 nicht ab, deshalb musst du res.ok selbst prüfen. await auf oberster Ebene (Top-Level-Await) funktioniert nur in ES-Modulen.

Parallel statt nacheinander

Wenn du unabhängige Aufgaben nacheinander mit await abwartest, verlierst du Zeit. Starte sie stattdessen gemeinsam:

const [user, posts] = await Promise.all([
  fetch('/api/user').then((r) => r.json()),
  fetch('/api/posts').then((r) => r.json()),
]);
  • Promise.all: wird beim ersten Fehler abgelehnt.
  • Promise.allSettled: wartet auf alle Promises und liefert für jedes Status und Wert bzw. Fehlergrund.
  • Promise.race / Promise.any: liefert das erste abgeschlossene bzw. das erste erfolgreiche Ergebnis.

Fallstricke

  • await innerhalb von forEach wartet nicht. Nutze for...of oder Promise.all mit map.
  • Ohne await bekommst du ein Promise statt des eigentlichen Werts.
  • Unbehandelte Ablehnungen lösen unhandledrejection aus. Fange Fehler deshalb immer mit catch oder try/catch ab.

Häufige Fragen

Wann nutze ich <code>async/await</code> statt <code>.then()</code>?

Fast immer. async/await liest wie synchroner Code, erleichtert Fehlerbehandlung (try/catch) und Schleifen über asynchrone Operationen. .then()-Ketten sind nur bei funktional-stilsicheren Pipelines idiomatischer.

Wie laufen mehrere Promises parallel?

Promise.all([p1, p2]) wartet auf alle und failed beim ersten Fehler. Promise.allSettled wartet auf alle, failed nie. Promise.race gibt das erste Resultat zurück. Promise.any den ersten Erfolg (ignoriert Fehler).

Was ist ein Unhandled-Promise-Rejection?

Ein Promise, das rejectet, ohne dass ein .catch() oder try/catch angehängt ist. In Node.js seit v15 standardmäßig ein Prozess-Fehler. Immer await in try/catch oder .catch() am Ende der Kette.

Blockiert <code>await</code> den Browser?

Nein — nur die ausführende async-Funktion pausiert. Der Event-Loop verarbeitet währenddessen UI, Timer und andere Tasks. Nur CPU-schwerer Sync-Code blockiert wirklich.

Wie breche ich eine <code>fetch</code>-Anfrage ab?

Mit einem AbortController: const c = new AbortController(); fetch(url, {signal: c.signal}). Dann c.abort(). Nützlich für Timeouts und Suchfeld-Entprellung.