JavaScript 8 — Asynchrones JavaScript

In dieser Lektion lernst du, wie JavaScript mit zeitlich verzögerten Abläufen umgeht. Asynchrones JavaScript ist entscheidend für reale Anwendungen: Netzwerkanfragen, Timer, Benutzerinteraktionen und Hintergrundprozesse würden ohne Asynchronität die gesamte Seite blockieren.

1. Das Problem: Blockierender Code

Definition.
JavaScript ist single-threaded: Es kann immer nur eine Sache gleichzeitig ausführen. Lange Aufgaben würden die Seite einfrieren, wenn sie synchron ausgeführt würden.
// Gedankenexperiment: blockiert die Seite
function longTask() {
  let start = Date.now();
  while (Date.now() - start < 5000) {}
  console.log("Fertig");
}

longTask();
console.log("Das kommt erst danach");
Während longTask läuft, reagiert die Seite nicht mehr. Genau das verhindert asynchrones JavaScript.

2. Das Event Loop Modell (Überblick)

JavaScript verwendet ein Event Loop-Modell, um asynchronen Code zu koordinieren.

Vereinfacht:

Du musst den Event Loop nicht im Detail implementieren, aber sein Prinzip verstehen.

3. setTimeout — der einfachste Einstieg

setTimeout führt eine Funktion nach einer bestimmten Zeit aus (asynchron).
console.log("Start");

setTimeout(() => {
  console.log("Nach 2 Sekunden");
}, 2000);

console.log("Ende");
Die Ausgabe zeigt: setTimeout blockiert nicht.

4. Callbacks

Ein Callback ist eine Funktion, die an eine andere Funktion übergeben wird und später aufgerufen wird.
function doSomethingLater(callback) {
  setTimeout(() => {
    callback("Fertig!");
  }, 1000);
}

doSomethingLater(message => {
  console.log(message);
});

4.1 Callback-Hell

Viele verschachtelte Callbacks führen zu schwer lesbarem Code („Callback-Hell“).
step1(() => {
  step2(() => {
    step3(() => {
      console.log("Done");
    });
  });
});

5. Promises

Ein Promise repräsentiert einen Wert, der jetzt noch nicht, aber später verfügbar sein wird.

Ein Promise hat drei Zustände:

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Erfolg!");
  }, 1000);
});

promise.then(result => {
  console.log(result);
});

5.1 Fehlerbehandlung mit catch

promise
  .then(result => console.log(result))
  .catch(error => console.error(error));

6. Verkettung von Promises

Promises können verkettet werden, um mehrere asynchrone Schritte nacheinander auszuführen.
fetchData()
  .then(data => processData(data))
  .then(result => console.log(result))
  .catch(err => console.error(err));
Jedes then gibt wieder ein Promise zurück.

7. async & await

async und await sind Syntax-Zucker für Promises und machen asynchronen Code lesbar wie synchronen.
async function loadData() {
  const result = await promise;
  console.log(result);
}

loadData();

7.1 Fehlerbehandlung mit try/catch

async function load() {
  try {
    const data = await fetchData();
    console.log(data);
  } catch (err) {
    console.error(err);
  }
}
await darf nur innerhalb von async-Funktionen verwendet werden.

8. Parallel vs. Sequenziell

Asynchrone Aufgaben können nacheinander oder parallel ausgeführt werden.

8.1 Sequenziell

const a = await taskA();
const b = await taskB();

8.2 Parallel

const [a, b] = await Promise.all([
  taskA(),
  taskB()
]);
Parallelisierung spart Zeit, wenn Aufgaben unabhängig sind.

9. Typische Fehler

10. Übungen

Übung 1.
Gib mit setTimeout nach 3 Sekunden eine Nachricht aus.
Übung 2.
Schreibe eine Funktion, die ein Promise zurückgibt und nach 1 Sekunde einen Text liefert.
Übung 3.
Wandle Übung 2 in async/await um und fange Fehler sauber ab.

11. Zusammenfassung

Wer asynchrones JavaScript beherrscht, beherrscht reale Anwendungen.