Aufgaben-Python/courses/javascript/05_callbacks_und_promises/README.md

131 lines
5.2 KiB
Markdown

# 🌐 JS 05: Callbacks, Event Loop & Promises
Willkommen zu **Modul 05** des JavaScript-Kurses! In diesem Kapitel lernst du das Herzstück der modernen Web-Entwicklung kennen: **Asynchrone Programmierung mit Promises**.
---
## 📖 1. Synchron vs. Asynchron & Der Event Loop
JavaScript führt Code in einer einzigen Hauptausführungsschleife aus (**Single-Threaded**).
- **Synchrone Ausführung (Blockierend):** Jede Codezeile wird nacheinander ausgeführt. Dauert eine Operation lange (z. B. ein schwerer Rechenschritt oder ein synchrones Warten), friert das gesamte Programm (und die Webseite!) ein.
- **Asynchrone Ausführung (Nicht-blockierend):** Zeitintensive Aufgaben (z. B. Netzwerk-Anfragen, Timer oder Dateizugriffe) werden im Hintergrund angestoßen. JavaScript läuft währenddessen sofort weiter. Sobald das Ergebnis vorliegt, wird ein Callback in die Warteschlange gelegt und vom **Event Loop** abgearbeitet.
```javascript
console.log("1. Bestellung aufgeben");
setTimeout(() => {
console.log("3. Essen ist fertig!"); // Asynchroner Callback nach 1000ms
}, 1000);
console.log("2. An den Tisch setzen und plaudern");
// Ausgabe-Reihenfolge: 1 -> 2 -> 3
```
---
## 🍔 2. Die Restaurant-Pieper-Analogie
Stell dir vor, du bestellst Burger in einem Selbstbedienungs-Restaurant:
1. **Bestellung:** Du gehst an die Kasse und bestellst dein Menü.
2. **Der Pieper (`Promise` im Zustand `pending`):** Die Bedienung gibt dir sofort einen elektronischen Pieper in die Hand. Du musst *nicht* stur am Tresen stehen bleiben und die Schlange blockieren.
3. **Nicht-blockierendes Warten:** Du setzt dich an deinen Tisch, surfst am Smartphone oder unterhältst dich mit Freunden (JavaScript Event Loop verarbeitet andere Ereignisse).
4. **Erfolg (`resolve` -> `fulfilled`):** Der Pieper vibriert und leuchtet grün! Du holst dein frisches Essen ab (`.then(essen => ...)`).
5. **Fehler (`reject` -> `rejected`):** Der Pieper piept rot und die Bedienung ruft: *"Die Burger-Patties sind leider aus!"* (`.catch(fehler => ...)`).
---
## ⏳ 3. Was ist ein Promise?
Ein **Promise** (deutsch: *Versprechen*) ist ein Objekt, das einen Wert repräsentiert, der jetzt, in der Zukunft oder nie verfügbar ist.
### Die 3 Zustände eines Promise:
```
┌───> fulfilled (erfolgreich gelöst) ──> .then(wert)
pending ──────┤
(schwebend) └───> rejected (fehlgeschlagen) ──> .catch(fehler)
```
1. **`pending` (schwebend):** Die asynchrone Operation läuft noch. Weder Erfolg noch Fehler.
2. **`fulfilled` (erfüllt):** Die Operation war erfolgreich, `resolve(wert)` wurde aufgerufen.
3. **`rejected` (abgewiesen):** Ein Fehler ist aufgetreten, `reject(error)` wurde aufgerufen.
---
## 🛠️ 4. Eigene Promises erstellen
Ein Promise wird mit dem Konstruktor `new Promise(executor)` erzeugt. Die Executor-Funktion erhält zwei Parameter: `resolve` und `reject`.
```javascript
function simuliereDatenAbruf(erfolgreich) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (erfolgreich) {
resolve({ id: 1, name: "Max Mustermann" });
} else {
reject(new Error("Netzwerk-Timeout beim Laden!"));
}
}, 500);
});
}
```
---
## 🔗 5. Promise-Chaining: `.then()`, `.catch()`, `.finally()`
Promises ermöglichen sauberes Verketten ohne verschachtelte Callback-Pyramiden (*Callback Hell*):
```javascript
simuliereDatenAbruf(true)
.then((daten) => {
console.log("Erhaltene Daten:", daten.name);
return daten.id; // Rückgabewert wird als nächstes Promise weitergegeben
})
.then((userId) => {
console.log("Lade Details für ID:", userId);
})
.catch((err) => {
console.error("Ein Fehler ist aufgetreten:", err.message);
})
.finally(() => {
console.log("Lade-Indikator ausblenden (wird immer ausgeführt)");
});
```
---
## ⚡ 6. Parallele Ausführung mit `Promise.all()` und `Promise.race()`
### `Promise.all([p1, p2, ...])`
Führt mehrere Promises parallel aus und wartet, bis **alle** erfolgreich aufgelöst wurden.
- Gibt ein Array mit allen Einzelergebnissen `[res1, res2, ...]` zurück.
- **Fail-Fast:** Bricht sofort ab, sobald *auch nur ein einzelnes* Promise rejected!
```javascript
const p1 = fetchUser();
const p2 = fetchPosts();
Promise.all([p1, p2])
.then(([user, posts]) => {
console.log("User & Posts gleichzeitig geladen:", user, posts);
})
.catch(err => console.error("Mindestens eine Quelle schlug fehl:", err));
```
### `Promise.race([p1, p2, ...])`
Liefert das Ergebnis des **allerschnellsten** Promise (egal ob `resolve` oder `reject`). Ideal z. B. für Timeouts.
---
## 🎯 7. Deine Aufgaben in `aufgabe.js`
1. **`verzoegereAusfuehrung(ms, rueckgabeWert)`**:
Erzeuge ein Promise, das nach `ms` Millisekunden mittels `setTimeout` den `rueckgabeWert` auflöst.
2. **`pruefeZahlGroesserNull(zahl)`**:
Prüfe die Zahl: Resolve bei `zahl > 0`, Reject mit `new Error("Zahl muss größer als 0 sein")` bei `<= 0`.
3. **`ladeBenutzerProfil(userId)`**:
Asynchrones Profil-Laden: Resolve mit `{ id: userId, name: "Benutzer_" + userId, status: "aktiv" }` bei `userId > 0`, sonst Reject mit `new Error("Ungültige User-ID")`.
4. **`ladeAlleDatenQuellen(quelleA, quelleB)`**:
Nutze `Promise.all([quelleA, quelleB])`, um zwei asynchrone Datenströme parallel zu aggregieren.