203 lines
8.8 KiB
HTML
203 lines
8.8 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>JS 05: Callbacks, Event Loop & Promises</title>
|
|
<link rel="stylesheet" href="../../../assets/style.css">
|
|
<style>
|
|
.promise-diagram {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 16px;
|
|
margin: 24px 0;
|
|
flex-wrap: wrap;
|
|
}
|
|
.state-box {
|
|
padding: 16px 20px;
|
|
border-radius: var(--radius-md);
|
|
font-weight: 700;
|
|
text-align: center;
|
|
min-width: 150px;
|
|
border: 2px solid transparent;
|
|
box-shadow: var(--shadow-sm);
|
|
}
|
|
.state-pending {
|
|
background: #fef3c7;
|
|
color: #92400e;
|
|
border-color: #f59e0b;
|
|
}
|
|
.state-fulfilled {
|
|
background: #dcfce7;
|
|
color: #166534;
|
|
border-color: #22c55e;
|
|
}
|
|
.state-rejected {
|
|
background: #ffe4e6;
|
|
color: #9f1239;
|
|
border-color: #f43f5e;
|
|
}
|
|
.diagram-arrow {
|
|
font-size: 1.5rem;
|
|
color: var(--text-muted);
|
|
font-weight: 800;
|
|
}
|
|
.analogy-box {
|
|
background: linear-gradient(135deg, #f8fafc 0%, #eef2ff 100%);
|
|
border-left: 4px solid var(--primary);
|
|
border-radius: var(--radius-sm);
|
|
padding: 18px 20px;
|
|
margin: 18px 0;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="app-layout">
|
|
<div class="app-main">
|
|
<header>
|
|
<div class="header-container">
|
|
<div class="header-badge">🌐 JavaScript Asynchronität</div>
|
|
<h1>JS 05: Callbacks, Event Loop & Promises</h1>
|
|
<p>Meistere nicht-blockierenden Code, den Event Loop und moderne Promises für robuste Web-Anwendungen.</p>
|
|
</div>
|
|
</header>
|
|
|
|
<main class="container">
|
|
<!-- Abschnitt 1: Konzept & Event Loop -->
|
|
<section class="card">
|
|
<h2>⚡ 1. Synchron vs. Asynchron & Event Loop</h2>
|
|
<p>
|
|
JavaScript ist <strong>Single-Threaded</strong>. Wenn eine Aufgabe Zeit benötigt (z. B. Timer, Netzwerk-Aufrufe, Dateilesen), darf sie den Hauptthread nicht blockieren.
|
|
</p>
|
|
<p>
|
|
Der <strong>Event Loop</strong> sorgt dafür, dass asynchrone Callbacks in einer Warteschlange verwaltet und erst ausgeführt werden, wenn der Haupt-Callstack frei ist.
|
|
</p>
|
|
|
|
<div class="analogy-box">
|
|
<h3>🍔 Die Restaurant-Pieper-Analogie</h3>
|
|
<p>
|
|
Bestellst du in einem Restaurant dein Essen, musst du <em>nicht</em> starr an der Kasse stehen bleiben. Du erhältst sofort einen <strong>Bestell-Pieper (ein Promise im Zustand <code>pending</code>)</strong>. Du setzt dich entspannt an deinen Tisch (der Event Loop verarbeitet andere Dinge).
|
|
</p>
|
|
<p style="margin-bottom: 0;">
|
|
Ist dein Menü fertig, vibriert der Pieper (<strong><code>resolve</code> → <code>fulfilled</code></strong>). Brennt die Küche, blinkt er rot (<strong><code>reject</code> → <code>rejected</code></strong>).
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Abschnitt 2: Was ist ein Promise & Zustandsdiagramm -->
|
|
<section class="card">
|
|
<h2>📊 2. Das Promise-Zustandsdiagramm</h2>
|
|
<p>
|
|
Ein <code>Promise</code> ist ein Stellvertreter-Objekt für einen zukünftigen Wert. Es befindet sich immer in genau einem von drei Zuständen:
|
|
</p>
|
|
|
|
<div class="promise-diagram">
|
|
<div class="state-box state-pending">
|
|
⏳ pending<br>
|
|
<small>(wartend / schwebend)</small>
|
|
</div>
|
|
<div style="display: flex; flex-direction: column; gap: 14px; align-items: center;">
|
|
<div style="display: flex; align-items: center; gap: 8px;">
|
|
<span class="diagram-arrow">→</span>
|
|
<span style="font-size: 0.85rem; font-weight: 700; color: #166534;">resolve(wert)</span>
|
|
<span class="diagram-arrow">→</span>
|
|
<div class="state-box state-fulfilled">
|
|
✅ fulfilled<br>
|
|
<small>.then(wert => ...)</small>
|
|
</div>
|
|
</div>
|
|
<div style="display: flex; align-items: center; gap: 8px;">
|
|
<span class="diagram-arrow">→</span>
|
|
<span style="font-size: 0.85rem; font-weight: 700; color: #9f1239;">reject(error)</span>
|
|
<span class="diagram-arrow">→</span>
|
|
<div class="state-box state-rejected">
|
|
❌ rejected<br>
|
|
<small>.catch(err => ...)</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Abschnitt 3: Code-Muster & Chaining -->
|
|
<section class="card">
|
|
<h2>🛠️ 3. Eigene Promises & Chaining</h2>
|
|
<p>Ein Promise wird mit <code>new Promise((resolve, reject) => { ... })</code> erzeugt:</p>
|
|
|
|
<pre><code>// 1. Promise definieren
|
|
function ladeDaten(id) {
|
|
return new Promise((resolve, reject) => {
|
|
if (id > 0) {
|
|
setTimeout(() => resolve({ id: id, status: "ok" }), 100);
|
|
} else {
|
|
reject(new Error("ID muss positiv sein"));
|
|
}
|
|
});
|
|
}
|
|
|
|
// 2. Promise verketten (Chaining)
|
|
ladeDaten(42)
|
|
.then(daten => {
|
|
console.log("Geladen:", daten);
|
|
return daten.id * 2;
|
|
})
|
|
.then(verdoppelt => console.log("Verdoppelt:", verdoppelt))
|
|
.catch(err => console.error("Fehler:", err.message))
|
|
.finally(() => console.log("Vorgang abgeschlossen."));</code></pre>
|
|
|
|
<h3 style="margin-top: 24px;">🚀 Parallelisierung mit <code>Promise.all()</code></h3>
|
|
<pre><code>// Lädt quelleA und quelleB parallel und wartet auf beide
|
|
Promise.all([quelleA, quelleB])
|
|
.then(([resA, resB]) => console.log("Beide fertig:", resA, resB))
|
|
.catch(err => console.error("Mindestens eine Quelle schlug fehl:", err));</code></pre>
|
|
</section>
|
|
|
|
<!-- Abschnitt 4: Aufgaben & Checkliste -->
|
|
<section class="card">
|
|
<h2>🎯 Aufgaben & Checkliste</h2>
|
|
<div class="task-item">
|
|
<input type="checkbox" class="task-checkbox" id="t1">
|
|
<label class="task-label" for="t1">
|
|
<strong>TODO 1:</strong> <code>verzoegereAusfuehrung(ms, rueckgabeWert)</code> mit <code>setTimeout</code> & <code>resolve</code> bauen.
|
|
</label>
|
|
</div>
|
|
<div class="task-item">
|
|
<input type="checkbox" class="task-checkbox" id="t2">
|
|
<label class="task-label" for="t2">
|
|
<strong>TODO 2:</strong> <code>pruefeZahlGroesserNull(zahl)</code> mit <code>resolve</code> bei > 0 bzw. <code>reject</code> bei <= 0 schreiben.
|
|
</label>
|
|
</div>
|
|
<div class="task-item">
|
|
<input type="checkbox" class="task-checkbox" id="t3">
|
|
<label class="task-label" for="t3">
|
|
<strong>TODO 3:</strong> <code>ladeBenutzerProfil(userId)</code> simulieren und Profil-Objekt auflösen.
|
|
</label>
|
|
</div>
|
|
<div class="task-item">
|
|
<input type="checkbox" class="task-checkbox" id="t4">
|
|
<label class="task-label" for="t4">
|
|
<strong>TODO 4:</strong> <code>ladeAlleDatenQuellen(quelleA, quelleB)</code> mittels <code>Promise.all()</code> parallel aggregieren.
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Lösungstipps Collapsible -->
|
|
<details style="margin-top: 20px; padding: 14px; background: #f8fafc; border: 1px solid var(--border-color); border-radius: var(--radius-sm);">
|
|
<summary style="cursor: pointer; font-weight: 700; color: var(--primary);">💡 Lösungstipps & Hilfestellung</summary>
|
|
<ul style="margin-top: 10px; margin-left: 20px; font-size: 0.92rem; color: var(--text-body);">
|
|
<li>Nutze <code>return new Promise((resolve, reject) => { ... });</code>.</li>
|
|
<li>Innerhalb des Executors rufst du <code>resolve(wert)</code> bei Erfolg oder <code>reject(new Error("..."))</code> bei Fehlern auf.</li>
|
|
<li><code>Promise.all</code> nimmt ein Array entgegen: <code>Promise.all([quelleA, quelleB])</code>.</li>
|
|
</ul>
|
|
</details>
|
|
|
|
<div style="margin-top: 24px; display: flex; gap: 12px; flex-wrap: wrap;">
|
|
<a href="../../../workspace.html?course=javascript&track=track_2_async_und_apis&chapter=05_callbacks_und_promises" class="btn" style="background: #eab308; color: #000; font-weight: 700; text-decoration: none; padding: 10px 20px; border-radius: 6px;">💻 In Web-IDE öffnen →</a>
|
|
<a href="../06_async_await_und_fetch/index.html" class="btn" style="background: #4f46e5; color: #fff; font-weight: 600; text-decoration: none; padding: 10px 20px; border-radius: 6px;">Weiter zu Modul 06 →</a>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|