EMZETT.
Login

Kurz: async/await ist eine Schreibweise, mit der asynchroner Code wie normaler, nacheinander ablaufender Code aussieht. Sie baut auf Promises auf.

Teil des Kurses JavaScript

Kapitel 26 von 40 im Kurs JavaScript. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

async/await ist eine Schreibweise, mit der asynchroner Code wie normaler, nacheinander ablaufender Code aussieht. Sie baut auf Promises auf.

Grundidee

  • Eine async-Funktion liefert immer ein Promise.
  • Mit await wartet die Funktion auf ein Promise, ohne den Rest des Programms zu blockieren.
const warte = (ms, wert) => new Promise(r => setTimeout(() => r(wert), ms));
 
async function ablauf() {
  console.log("start");
  const a = await warte(50, "A");
  console.log("erhalten:", a);
  const b = await warte(10, "B");
  return a + b;
}
 
ablauf().then(erg => console.log("Ergebnis:", erg));
console.log("läuft schon weiter");

Ausgabe:

start
läuft schon weiter
erhalten: A
Ergebnis: AB

Fehler abfangen

Bei await nutzt du ganz normal try/catch:

async function laden() {
  try {
    const r = await Promise.reject(new Error("Server nicht erreichbar"));
    console.log(r);
  } catch (e) {
    console.log("Fehler:", e.message);
  } finally {
    console.log("fertig");
  }
}
laden();

Ausgabe:

Fehler: Server nicht erreichbar
fertig

Nacheinander oder parallel?

Mehrere await hintereinander laufen nacheinander. Unabhängige Aufgaben startest du besser gleichzeitig:

const task = (name, ms) => new Promise(r => setTimeout(() => r(name), ms));
 
async function main() {
  let t0 = Date.now();
  await task("a", 100);
  await task("b", 100);
  console.log("nacheinander ~200 ms:", Date.now() - t0 >= 190);
 
  t0 = Date.now();
  const [x, y] = await Promise.all([task("a", 100), task("b", 100)]);
  console.log("parallel ~100 ms:", Date.now() - t0 < 190, x, y);
}
main();

Ausgabe:

nacheinander ~200 ms: true
parallel ~100 ms: true a b

Achtung

await in forEach funktioniert nicht wie erwartet, weil forEach nicht wartet. Nutze for...of mit await oder Promise.all mit map.

const sleep = ms => new Promise(r => setTimeout(r, ms));
(async () => {
  for (const x of [1, 2, 3]) {       // nacheinander
    await sleep(5);
    console.log("seq", x);
  }
  const alle = await Promise.all([1, 2, 3].map(async x => { await sleep(5); return x * 2; }));
  console.log(alle);
})();

Ausgabe:

seq 1
seq 2
seq 3
[ 2, 4, 6 ]

Daten aus dem Netz: fetch

fetch() holt Daten per HTTP und liefert ein Promise. Es funktioniert im Browser und in Node.js (ab Version 18):

async function ladeNutzer(id) {
  const antwort = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
  if (!antwort.ok) {
    throw new Error(`HTTP-Fehler ${antwort.status}`);
  }
  const daten = await antwort.json();
  return daten.name;
}
 
ladeNutzer(1)
  .then(name => console.log(name))
  .catch(fehler => console.log("Fehler:", fehler.message));

Wichtig: fetch löst nur bei Netzwerkfehlern einen Fehler aus. Eine Antwort mit Status 404 oder 500 ist trotzdem eine “erfolgreiche” Antwort, daher prüfst du antwort.ok selbst.

Daten senden

const antwort = await fetch("/api/nachrichten", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ text: "Hallo" }),
});
const ergebnis = await antwort.json();
Antwort-Methodeliefert
antwort.json()geparstes JSON
antwort.text()Text
antwort.blob()Binärdaten (Bilder)
antwort.status, antwort.okStatuscode, Erfolg (200 bis 299)

Abbrechen und Zeitlimit

const warte = ms => new Promise((_, rej) => setTimeout(() => rej(new Error("Zeitüberschreitung")), ms));
const langsam = new Promise(r => setTimeout(() => r("fertig"), 500));
 
Promise.race([langsam, warte(50)])
  .then(console.log)
  .catch(e => console.log(e.message));
 
const abbruch = new AbortController();
abbruch.signal.addEventListener("abort", () => console.log("abgebrochen:", abbruch.signal.aborted));
abbruch.abort();

Ausgabe:

abgebrochen: true
Zeitüberschreitung

Bei fetch übergibst du { signal: controller.signal }, um eine Anfrage abzubrechen. AbortSignal.timeout(5000) bricht automatisch nach 5 Sekunden ab.

Top-Level await

In ES-Modulen darfst du await auch außerhalb einer Funktion verwenden:

// in einer .mjs-Datei oder <script type="module">
const daten = await fetch("/daten.json").then(r => r.json());

Merke

  • async macht eine Funktion zu einer, die ein Promise liefert
  • await wartet auf ein Promise, ohne das Programm zu blockieren
  • Fehler mit try/catch abfangen
  • Unabhängiges mit Promise.all parallel ausführen
  • fetch wirft bei 404/500 nicht: antwort.ok prüfen

Übungsaufgabe

Schreibe eine async-Funktion, die drei Wartezeiten parallel startet, mit Promise.all wartet und die Gesamtdauer ausgibt. Baue einen Fehlerfall mit try/catch ein.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Asynchron: Callbacks und Promises

Weiter: Timer und die Event Loop

Alle Kapitel: JavaScript im Überblick