⏳Asynchrones JavaScript
Warten, ohne zu blockieren: Während ein Timer läuft oder eine Anfrage unterwegs ist, bleibt die Seite bedienbar. Die Werkzeuge dafür haben sich entwickelt – von Callbacks über Promises zu async/await.
🪜Callbacks → Promises → async/await
1 · Callbacks
1function lade(id, fertig) {2 setTimeout(() => fertig(null, { id, name: "Nutzer " + id }), 20);3}4lade(1, (err, nutzer) => {5 if (err) return console.log("Fehler", err);6 console.log("geladen:", nutzer.name);7 lade(2, (err, zweiter) => { // Verschachtelung wächst …8 console.log("geladen:", zweiter.name);9 });10});
Jeder weitere Schritt rückt eine Ebene ein („Callback-Hölle“); Fehler muss man überall einzeln prüfen.
2 · Promises
1function lade(id) {2 return new Promise((resolve) =>3 setTimeout(() => resolve({ id, name: "Nutzer " + id }), 20));4}5lade(1)6 .then((n) => { console.log("geladen:", n.name); return lade(2); })7 .then((n) => console.log("geladen:", n.name))8 .catch((e) => console.log("Fehler", e));
then gibt ein neues Promise zurück – die Kette bleibt flach, ein catch am Ende fängt alle Fehler.
3 · async/await
1const lade = (id) => new Promise((resolve) =>2 setTimeout(() => resolve({ id, name: "Nutzer " + id }), 20));34async function main() {5 try {6 const n1 = await lade(1);7 console.log("geladen:", n1.name);8 const n2 = await lade(2);9 console.log("geladen:", n2.name);10 } catch (e) {11 console.log("Fehler", e);12 }13}14main();
Liest sich wie synchroner Code, ist aber dieselbe Promise-Mechanik darunter. Fehler fängt ein normales try/catch.
🚦Die drei Zustände eines Promise
Ein Promise ist genau einmal erledigt: Nach resolve oder reject ändert sich der Zustand nie wieder – weitere Aufrufe werden ignoriert. Die Callbacks laufen als Microtask, also erst nachdem der aktuelle Code fertig ist.
🏁Promise.all, allSettled, race und any als Zeitleiste
Alle müssen klappen. Erste Ablehnung → sofort abgelehnt.
B wird nach 500 ms abgelehnt → Promise.all bricht sofort ab („fail fast“). Die übrigen Ergebnisse werden ignoriert.
Wartet auf alle, wird immer erfüllt – mit Statusobjekten.
Wartet auf alle (1200 ms) und wird immer erfüllt – mit einem Statusobjekt je Eingabe.
Das erste erledigte Promise entscheidet – erfüllt oder abgelehnt.
B ist nach 500 ms als Erstes erledigt – egal ob erfüllt oder abgelehnt.
Das erste erfüllte gewinnt. Nur wenn alle scheitern: AggregateError.
C ist nach 800 ms das erste erfüllte Promise. Ablehnungen davor werden ignoriert.
📡fetch gegen die Beispiel-API
1const res = await fetch("/api/beispiele");2console.log(res.status, res.ok);3const daten = await res.json(); // zweites await: Body lesen4console.log(daten.anzahl, daten.beispiele[0].titel);
💡 fetch liefert zuerst nur die Antwort-Kopfzeilen (Response). Den Body liest man mit einem zweiten await (res.json(), res.text() …).
🧯Fehler in asynchronem Code
try/catch fängt nur mit await
1const kaputt = () => Promise.reject(new Error("Netz weg"));23async function mitAwait() {4 try { await kaputt(); }5 catch (e) { console.log("gefangen:", e.message); }6}7async function ohneAwait() {8 try { return kaputt(); } // kein await!9 catch (e) { console.log("wird nie erreicht"); }10}11await mitAwait();12await ohneAwait().catch((e) => console.log("erst außen:", e.message));
Ohne await verlässt das abgelehnte Promise die Funktion, bevor try/catch es sehen kann. Im Zweifel: return await.
Unbehandelte Ablehnung
1Promise.reject(new Error("niemand hört zu"));2setTimeout(() => console.log("Programm läuft weiter"), 10);
Browser melden „Uncaught (in promise)“ in der Konsole, Node.js beendet den Prozess seit Version 15 standardmäßig.
Nacheinander vs. parallel
1const warte = (ms) => new Promise((r) => setTimeout(() => r(ms), ms));2let t = Date.now();3await warte(100); await warte(100);4console.log("nacheinander ≥ 200 ms:", Date.now() - t >= 200);5t = Date.now();6await Promise.all([warte(100), warte(100)]);7console.log("parallel < 200 ms:", Date.now() - t < 200);
Unabhängige Aufgaben nicht einzeln awaiten, sondern gemeinsam starten und mit Promise.all abwarten.
await in try/catch oder ein .catch() am Ende der Kette. Kein async-Callback in forEach – das wartet nicht; stattdessen for…of mit await oder Promise.all.