Appearance
Kapitel 6: Asynchrone Programmierung
🎯 Lernziele
In diesem Kapitel lernen Sie:
- ✅ Synchron vs. Asynchron verstehen
- ✅ Blockierend vs. Nicht-blockierend
- ✅ Callback-Funktionen
- ✅ Promises (Versprechen)
- ✅ async/await (Moderne Methode)
- ✅ "Callback Hell" vermeiden
- ✅ Häufige Fehler bei asynchronem Code
6.1 Synchron vs. Asynchron
📖 Einfache Definition
| Begriff | Bedeutung | Beispiel |
|---|---|---|
| Synchron | Code wird zeilenweise ausgeführt (nacheinander) | Warten auf Dateilesen |
| Asynchron | Code wird nicht blockiert ausgeführt | Weiterarbeiten während Datei liest |
🎨 Visuelle Erklärung
Synchrone Ausführung (Blockierend):
┌─────────────────────────────────┐
│ Task 1: Datei lesen... │ ← Wartet!
│ Task 2: Berechnung │ ← Muss warten
│ Task 3: Antwort senden │ ← Muss warten
└─────────────────────────────────┘
Gesamtzeit = 10s + 2s + 1s = 13s
Asynchrone Ausführung (Nicht-blockierend):
┌─────────────────────────────────┐
│ Task 1: Datei lesen (Start) │ ← Startet, läuft im Hintergrund
│ Task 2: Berechnung │ ← Sofort ausgeführt!
│ Task 3: Antwort senden │ ← Nach Task 2
│ ... (Datei fertig) ... │ ← Wenn fertig, Callback ausführen
└─────────────────────────────────┘
Gesamtzeit ≈ 2s (Parallel!)💡 Code-Beispiel
javascript
// ❌ Synchrone Methode (Blockierend - Schlecht!)
const fs = require('fs');
console.log('1. Starte Dateilesen...');
const daten = fs.readFileSync('datei.txt', 'utf8'); // Blockiert!
console.log('2. Datei gelesen:', daten);
console.log('3. Weiter mit anderem Code...'); // Muss warten!
// ✅ Asynchrone Methode (Nicht-blockierend - Gut!)
console.log('1. Starte Dateilesen...');
fs.readFile('datei.txt', 'utf8', (err, daten) => {
console.log('2. Datei gelesen:', daten);
});
console.log('3. Weiter mit anderem Code...'); // Wird sofort ausgeführt!Ausgabe (Asynchron):
1. Starte Dateilesen...
3. Weiter mit anderem Code...
2. Datei gelesen: [Inhalt]6.2 Drei Methoden
📚 Methode 1: Callback-Funktionen (Traditionell)
Callback = Eine Funktion, die als Argument an eine andere Funktion übergeben wird.
javascript
// Grundlegende Callback-Struktur
function dateiLesen(pfad, callback) {
fs.readFile(pfad, 'utf8', (err, daten) => {
if (err) {
callback(err, null); // Fehler weitergeben
return;
}
callback(null, daten); // Ergebnis weitergeben
});
}
// Verwendung
dateiLesen('datei.txt', (err, daten) => {
if (err) {
console.error('Fehler:', err);
return;
}
console.log('Inhalt:', daten);
});✅ Vorteile
- Einfach zu verstehen (für kleine Aufgaben)
- Keine zusätzlichen Bibliotheken nötig
❌ Nachteile
- "Callback Hell" bei vielen verschachtelten Callbacks
- Schwer zu lesen und zu warten
📚 Methode 2: Promises (Modern)
Promise = Ein Objekt, das einen zukünftigen Wert repräsentiert.
Promise-Zustände
Promise-Zustände:
┌─────────┐
│ Pending │ ← Wartend (noch nicht fertig)
└─────────┘
↓
┌─────────┐ oder ┌─────────┐
│ Fulfilled│ │ Rejected│
│ (Erfolg) │ │(Fehler) │
└─────────┘ └─────────┘Promise erstellen
javascript
// Promise erstellen
function dateiLesenAsync(pfad) {
return new Promise((resolve, reject) => {
fs.readFile(pfad, 'utf8', (err, daten) => {
if (err) {
reject(err); // Fehler
} else {
resolve(daten); // Erfolg
}
});
});
}
// Promise verwenden
dateiLesenAsync('datei.txt')
.then(daten => {
console.log('Erfolg:', daten);
})
.catch(err => {
console.error('Fehler:', err);
});Promise-Kette (Chaining)
javascript
// Mehrere asynchrone Operationen nacheinander
dateiLesenAsync('datei1.txt')
.then(daten1 => {
console.log('Datei 1:', daten1);
return dateiLesenAsync('datei2.txt'); // Nächstes Promise
})
.then(daten2 => {
console.log('Datei 2:', daten2);
return dateiLesenAsync('datei3.txt');
})
.then(daten3 => {
console.log('Datei 3:', daten3);
})
.catch(err => {
console.error('Fehler in einer der Dateien:', err);
});📚 Methode 3: async/await (Empfohlen!)
async/await = Die modernste und lesbarste Methode.
Grundlegende Syntax
javascript
// Funktion mit async markieren
async function meineFunktion() {
try {
// await = "Warte auf das Ergebnis"
const daten = await dateiLesenAsync('datei.txt');
console.log('Inhalt:', daten);
// Weitere asynchrone Operationen
const daten2 = await dateiLesenAsync('datei2.txt');
console.log('Inhalt 2:', daten2);
} catch (err) {
console.error('Fehler:', err);
}
}
// Funktion aufrufen
meineFunktion();✅ Vorteile von async/await
- Lesbar wie synchroner Code
- Fehlerbehandlung mit try/catch
- Kein Callback Hell
- Einfach zu debuggen
6.3 Callback Hell
❌ Was ist "Callback Hell"?
javascript
// ❌ Callback Hell (Pyramid of Doom)
fs.readFile('datei1.txt', 'utf8', (err1, daten1) => {
if (err1) {
console.error(err1);
return;
}
fs.readFile('datei2.txt', 'utf8', (err2, daten2) => {
if (err2) {
console.error(err2);
return;
}
fs.readFile('datei3.txt', 'utf8', (err3, daten3) => {
if (err3) {
console.error(err3);
return;
}
console.log('Alle Dateien gelesen:', daten1, daten2, daten3);
// Noch mehr Callbacks... 😱
});
});
});✅ Lösung 1: Promises verwenden
javascript
// ✅ Mit Promises (Flacher und lesbarer)
function leseDatei(pfad) {
return new Promise((resolve, reject) => {
fs.readFile(pfad, 'utf8', (err, daten) => {
if (err) reject(err);
else resolve(daten);
});
});
}
leseDatei('datei1.txt')
.then(daten1 => {
console.log('Datei 1:', daten1);
return leseDatei('datei2.txt');
})
.then(daten2 => {
console.log('Datei 2:', daten2);
return leseDatei('datei3.txt');
})
.then(daten3 => {
console.log('Datei 3:', daten3);
})
.catch(err => {
console.error('Fehler:', err);
});✅ Lösung 2: async/await verwenden (Beste Lösung!)
javascript
// ✅✅ Mit async/await (Beste Lesbarkeit!)
async function alleDateienLesen() {
try {
const daten1 = await leseDatei('datei1.txt');
console.log('Datei 1:', daten1);
const daten2 = await leseDatei('datei2.txt');
console.log('Datei 2:', daten2);
const daten3 = await leseDatei('datei3.txt');
console.log('Datei 3:', daten3);
console.log('Alle Dateien erfolgreich gelesen!');
} catch (err) {
console.error('Fehler beim Lesen:', err);
}
}
alleDateienLesen();6.4 Praxisbeispiele
📂 Fallstudie 1: Dateien parallel lesen
javascript
const fs = require('fs').promises; // Promise-basierte API
// Methode 1: Nacheinander lesen (Langsam)
async function nacheinander() {
console.time('Nacheinander');
const datei1 = await fs.readFile('datei1.txt', 'utf8');
const datei2 = await fs.readFile('datei2.txt', 'utf8');
const datei3 = await fs.readFile('datei3.txt', 'utf8');
console.timeEnd('Nacheinander');
return [datei1, datei2, datei3];
}
// Methode 2: Parallel lesen (Schnell!) ✅
async function parallel() {
console.time('Parallel');
const [datei1, datei2, datei3] = await Promise.all([
fs.readFile('datei1.txt', 'utf8'),
fs.readFile('datei2.txt', 'utf8'),
fs.readFile('datei3.txt', 'utf8')
]);
console.timeEnd('Parallel');
return [datei1, datei2, datei3];
}
// Test
parallel(); // Viel schneller!🌐 Fallstudie 2: HTTP-Request mit async/await
javascript
const https = require('https');
// Hilfsfunktion: HTTP-Request als Promise
function httpGet(url) {
return new Promise((resolve, reject) => {
https.get(url, (res) => {
let daten = '';
res.on('data', chunk => {
daten += chunk;
});
res.on('end', () => {
resolve(daten);
});
}).on('error', (err) => {
reject(err);
});
});
}
// Async/Await verwenden
async function apiAufrufen() {
try {
console.log('Rufe API auf...');
const ergebnis = await httpGet('https://api.github.com/users/github');
const json = JSON.parse(ergebnis);
console.log('API Ergebnis:');
console.log('Name:', json.name);
console.log('Followers:', json.followers);
} catch (err) {
console.error('Fehler beim API-Aufruf:', err.message);
}
}
apiAufrufen();6.5 Häufige Fehler
⚠️ Fehler 1: Vergessen von await
javascript
// ❌ Falsch: await vergessen
async function meinFehler() {
const daten = dateiLesenAsync('datei.txt'); // Promise-Objekt, nicht der Inhalt!
console.log(daten); // ← Promise { <pending> }
}
// ✅ Richtig: await verwenden
async function korrekt() {
const daten = await dateiLesenAsync('datei.txt');
console.log(daten); // ← Der tatsächliche Inhalt
}⚠️ Fehler 2: await außerhalb von async-Funktion
javascript
// ❌ Falsch: await in normaler Funktion
function meineFunktion() {
const daten = await dateiLesenAsync('datei.txt'); // SyntaxError!
}
// ✅ Richtig: Funktion muss async sein
async function meineFunktion() {
const daten = await dateiLesenAsync('datei.txt');
}⚠️ Fehler 3: Fehlerbehandlung fehlt
javascript
// ❌ Gefährlich: Keine Fehlerbehandlung
async function gefaehrlich() {
const daten = await dateiLesenAsync('datei.txt');
console.log(daten); // Wenn Datei nicht existiert → Crash!
}
// ✅ Sicher: try/catch verwenden
async function sicher() {
try {
const daten = await dateiLesenAsync('datei.txt');
console.log(daten);
} catch (err) {
console.error('Fehler:', err.message);
}
}⚠️ Fehler 4: await in Schleifen (Performance-Problem)
javascript
// ❌ Langsam: await in Schleife (Nacheinander)
async function langsam() {
const ergebnisse = [];
const urls = ['url1', 'url2', 'url3'];
for (const url of urls) {
const daten = await httpGet(url); // Wartet jedes Mal!
ergebnisse.push(daten);
}
return ergebnisse;
}
// ✅ Schnell: Promise.all verwenden (Parallel)
async function schnell() {
const urls = ['url1', 'url2', 'url3'];
const versprechen = urls.map(url => httpGet(url));
const ergebnisse = await Promise.all(versprechen); // Parallel!
return ergebnisse;
}📝 Zusammenfassung
In diesem Kapitel haben Sie gelernt:
- ✅ Synchron = Blockierend, Asynchron = Nicht-blockierend
- ✅ Callback-Funktionen (Traditionell, aber "Callback Hell")
- ✅ Promises (Besser, mit
.then()und.catch()) - ✅ async/await (Am besten, lesbar wie synchroner Code)
- ✅ "Callback Hell" vermeiden mit Promises oder async/await
- ✅ Häufige Fehler: await vergessen, try/catch vergessen
🎯 Nächste Schritte
Im nächsten Kapitel werden wir:
- HTTP-Server mit dem
http-Modul erstellen - Anfragen (
req) und Antworten (res) verarbeiten - Routing implementieren
📚 Weiterführende Ressourcen
🎉 Kapitel 6 abgeschlossen! Weiter zu Kapitel 7: HTTP-Server Entwicklung
