JavaScript Async/Await & Promises
JavaScript ist single-threaded, kann aber trotzdem mehrere Dinge "gleichzeitig" machen. Das Geheimnis? Asynchrone Programmierung!
Das Problem: Synchroner Code blockiert
console.log('Start');
// Simuliere lange Berechnung (3 Sekunden)
function longCalculation() {
const start = Date.now();
while (Date.now() - start < 3000) {
// Blockiert alles!
}
return 'Fertig';
}
console.log(longCalculation()); // 3 Sekunden Wartezeit
console.log('Ende');
// Output:
// Start
// ... 3 Sekunden Warten ...
// Fertig
// EndeProblem: Während longCalculation() läuft, ist die gesamte Seite eingefroren! ❌
Die Lösung: Asynchroner Code
Callbacks (Old School)
console.log('Start');
// Asynchrone Funktion mit Callback
function fetchData(callback) {
setTimeout(() => {
const data = { name: 'Max', age: 25 };
callback(data);
}, 2000);
}
fetchData((result) => {
console.log('Daten:', result);
});
console.log('Ende');
// Output:
// Start
// Ende
// ... 2 Sekunden später ...
// Daten: { name: 'Max', age: 25 }Vorteil: Code blockiert nicht mehr! ✅ Nachteil: "Callback Hell" bei verschachtelten Aufrufen ❌
Callback Hell - Das Problem
// ❌ Callback Hell - Pyramide des Grauens
getUser(1, (user) => {
getOrders(user.id, (orders) => {
getOrderDetails(orders[0].id, (details) => {
getShippingInfo(details.shippingId, (shipping) => {
console.log(shipping);
// ... noch mehr Verschachtelung ...
});
});
});
});Promises - Die moderne Lösung
Ein Promise ist ein Versprechen für einen zukünftigen Wert.
Promise States (Zustände)
pending → noch nicht fertig (Anfangszustand)
fulfilled → erfolgreich abgeschlossen (resolved)
rejected → fehlgeschlagen
Promise erstellen
// Promise erstellen
const myPromise = new Promise((resolve, reject) => {
// Asynchrone Operation
setTimeout(() => {
const success = true;
if (success) {
resolve('Erfolgreich!'); // Promise erfüllt
} else {
reject('Fehler aufgetreten!'); // Promise abgelehnt
}
}, 2000);
});
// Promise verwenden
myPromise
.then((result) => {
console.log(result); // "Erfolgreich!"
})
.catch((error) => {
console.error(error);
});Promise Chaining
// ✅ Promise Chain - Lesbar!
getUser(1)
.then((user) => {
console.log('User:', user);
return getOrders(user.id);
})
.then((orders) => {
console.log('Orders:', orders);
return getOrderDetails(orders[0].id);
})
.then((details) => {
console.log('Details:', details);
return getShippingInfo(details.shippingId);
})
.then((shipping) => {
console.log('Shipping:', shipping);
})
.catch((error) => {
console.error('Fehler:', error);
});Praktisches Beispiel: Daten laden
function fetchUserData(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (userId > 0) {
resolve({
id: userId,
name: 'Anna Schmidt',
email: 'anna@example.com'
});
} else {
reject('Ungültige User-ID');
}
}, 1000);
});
}
// Verwenden
fetchUserData(123)
.then((user) => {
console.log('Empfangen:', user);
})
.catch((error) => {
console.error('Fehler:', error);
});Async/Await - Noch besser!
Async/Await macht asynchronen Code wie synchronen Code aussehen!
Grundlagen
// Funktion mit 'async' markieren
async function loadData() {
// 'await' pausiert bis Promise fertig ist
const user = await fetchUserData(123);
console.log(user);
return user;
}
// Async-Funktionen geben IMMER ein Promise zurück
loadData()
.then(() => console.log('Fertig!'));Fehlerbehandlung mit try/catch
async function loadUserData() {
try {
const user = await fetchUserData(123);
console.log('User geladen:', user);
const orders = await fetchOrders(user.id);
console.log('Orders geladen:', orders);
return { user, orders };
} catch (error) {
console.error('Fehler beim Laden:', error);
throw error; // Weitergeben
}
}
// Verwenden
loadUserData()
.then((data) => {
console.log('Alle Daten:', data);
})
.catch((error) => {
console.error('Finaler Fehler:', error);
});Vorher vs. Nachher
// ❌ Mit Promises (funktioniert, aber unübersichtlich)
function getUserInfo(userId) {
return fetchUser(userId)
.then((user) => {
return fetchOrders(user.id)
.then((orders) => {
return { user, orders };
});
})
.catch((error) => {
console.error(error);
});
}
// ✅ Mit Async/Await (klar und lesbar!)
async function getUserInfo(userId) {
try {
const user = await fetchUser(userId);
const orders = await fetchOrders(user.id);
return { user, orders };
} catch (error) {
console.error(error);
}
}Multiple Promises gleichzeitig
Promise.all() - Warte auf alle
async function loadAllData() {
try {
// Alle Promises gleichzeitig starten!
const [user, posts, comments] = await Promise.all([
fetchUser(1),
fetchPosts(),
fetchComments()
]);
console.log('Alles geladen:', { user, posts, comments });
return { user, posts, comments };
} catch (error) {
// Wenn EINES fehlschlägt, wird alles abgebrochen
console.error('Fehler:', error);
}
}
loadAllData();Wichtig: Promise.all() schlägt fehl, wenn ein einziges Promise rejected wird!
Promise.allSettled() - Warte auf alle, egal ob Erfolg oder Fehler
async function loadDataSafely() {
const results = await Promise.allSettled([
fetchUser(1),
fetchUser(999), // Fehlschlägt
fetchPosts()
]);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Promise ${index}: Erfolg`, result.value);
} else {
console.log(`Promise ${index}: Fehler`, result.reason);
}
});
}
loadDataSafely();
// Output:
// Promise 0: Erfolg { id: 1, name: 'Max' }
// Promise 1: Fehler User not found
// Promise 2: Erfolg [{ id: 1, title: '...' }, ...]Promise.race() - Erster gewinnt
async function fastestServer() {
const result = await Promise.race([
fetchFromServer1(),
fetchFromServer2(),
fetchFromServer3()
]);
console.log('Schnellster Server antwortet:', result);
return result;
}
// Use Case: Timeout implementieren
async function fetchWithTimeout(url, timeout = 5000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject('Timeout!'), timeout)
)
]);
}Reale Beispiele
Beispiel 1: Daten von API laden
async function loadPosts() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
const posts = await response.json();
console.log('Posts geladen:', posts);
return posts;
} catch (error) {
console.error('Fehler beim Laden:', error);
throw error;
}
}
loadPosts();Beispiel 2: Sequentielles Laden mit Progress
async function loadDashboard() {
console.log('Lade Dashboard...');
// Schritt 1
console.log('1/3: Lade User...');
const user = await fetchUser(1);
console.log('✓ User geladen');
// Schritt 2
console.log('2/3: Lade Posts...');
const posts = await fetchPosts(user.id);
console.log('✓ Posts geladen');
// Schritt 3
console.log('3/3: Lade Statistiken...');
const stats = await fetchStats(user.id);
console.log('✓ Statistiken geladen');
console.log('Dashboard komplett!');
return { user, posts, stats };
}
loadDashboard();Beispiel 3: Retry-Logic
async function fetchWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
console.log(`Versuch ${i + 1}/${maxRetries}...`);
const response = await fetch(url);
if (response.ok) {
return await response.json();
}
} catch (error) {
console.error(`Versuch ${i + 1} fehlgeschlagen:`, error);
if (i === maxRetries - 1) {
throw new Error('Alle Versuche fehlgeschlagen');
}
// Warte 1 Sekunde vor erneutem Versuch
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
fetchWithRetry('https://api.example.com/data');📝 Quiz
Was gibt eine async-Funktion IMMER zurück?
Tipps & Tricks
Top-Level Await (Moderne Browser)
In modernen Browsern kannst du await auch außerhalb von async-Funktionen nutzen:
// In Modulen (type="module")
const data = await fetchData();
console.log(data);
Async/Await mit Array Methods
// ❌ FALSCH - forEach funktioniert nicht mit async!
array.forEach(async (item) => {
await processItem(item);
});
// ✅ RICHTIG - for...of verwenden
for (const item of array) {
await processItem(item);
}
// ✅ RICHTIG - Parallel mit Promise.all
await Promise.all(
array.map(item => processItem(item))
);
Debugging Async Code
async function debug() {
console.log('Start');
const result = await fetchData();
debugger; // Breakpoint hier setzen
console.log('Result:', result);
}
Promise.any() - Erste erfolgreiche
// Nimm erste erfolgreiche Response
const fastSuccess = await Promise.any([
fetchFromServer1(), // Langsam aber erfolgreich
fetchFromServer2(), // Schnell aber Fehler
fetchFromServer3() // Mittel und erfolgreich
]);
Häufige Fehler
Fehler 1: await ohne async
❌ FALSCH:
function getData() {
const data = await fetchData(); // ❌ SyntaxError!
return data;
}
✅ RICHTIG:
async function getData() {
const data = await fetchData(); // ✅
return data;
}
Fehler 2: await in Schleife (langsam!)
❌ FALSCH (langsam - nacheinander):
async function processUsers(users) {
const results = [];
for (const user of users) {
const data = await fetchUserData(user.id); // Wartet jedes Mal!
results.push(data);
}
return results;
}
✅ RICHTIG (schnell - parallel):
async function processUsers(users) {
const promises = users.map(user => fetchUserData(user.id));
const results = await Promise.all(promises);
return results;
}
Fehler 3: Fehlerbehandlung vergessen
❌ FALSCH:
async function loadData() {
const data = await fetchData(); // Was wenn Fehler?
return data;
}
✅ RICHTIG:
async function loadData() {
try {
const data = await fetchData();
return data;
} catch (error) {
console.error('Fehler:', error);
throw error;
}
}
Fehler 4: Promise nicht awaiten
❌ FALSCH:
async function saveData(data) {
saveToDatabase(data); // Promise wird nicht abgewartet!
console.log('Gespeichert'); // Lüge! Vielleicht noch nicht fertig
}
✅ RICHTIG:
async function saveData(data) {
await saveToDatabase(data); // Jetzt wirklich warten
console.log('Gespeichert'); // Stimmt!
}
Zusammenfassung
Du hast gelernt:
- ✅ JavaScript ist single-threaded, aber asynchron
- ✅ Callbacks → Promises → Async/Await (Evolution)
- ✅ Promises haben 3 Zustände: pending, fulfilled, rejected
- ✅
asyncmarkiert Funktion als asynchron - ✅
awaitpausiert bis Promise fertig ist - ✅
try/catchfür Fehlerbehandlung - ✅
Promise.all()für parallele Ausführung - ✅
Promise.race()für schnellste Response
Key Takeaways:
- Async-Funktionen returnen IMMER ein Promise
awaitnur inasync-Funktionen- Immer Fehlerbehandlung mit try/catch
- Promise.all() für parallele Requests (schneller!)
- await in Schleifen vermeiden (langsam!)
Best Practices:
- Nutze async/await statt Promises (lesbarer)
- Mehrere Requests? Promise.all() nutzen
- Immer try/catch verwenden
- Timeout mit Promise.race() implementieren
Praktische Übungen
Übung 1: Einfaches Promise
Erstelle ein Promise, das nach 2 Sekunden "Hallo Welt" zurückgibt.
Übung 2: API-Anfrage
Lade Daten von https://jsonplaceholder.typicode.com/users mit async/await.
Übung 3: Parallele Requests
Lade gleichzeitig:
- Users
- Posts
- Comments
Mit Promise.all().
Übung 4: Fehlerbehandlung
Schreibe eine Funktion die:
- Daten lädt
- Bei Fehler 3x wiederholt
- Nach 3 Fehlversuchen aufgibt
Übung 5: Loading Spinner
Erstelle:
async function loadDataWithSpinner() {
showSpinner();
try {
const data = await fetchData();
displayData(data);
} catch (error) {
showError(error);
} finally {
hideSpinner(); // Immer ausführen!
}
}
Gut gemacht! 🎉
Du hast "JavaScript Async/Await & Promises - Asynchrone Programmierung" abgeschlossen
Artikel bewerten
Bitte einloggen um zu bewerten
Das könnte dich auch interessieren
JavaScript Array Methods - map, filter, reduce & mehr
Meistere die wichtigsten JavaScript Array Methods. Von map über filter bis reduce - alles verständlich erklärt mit praktischen Beispielen.
JavaScript ES6+ Features - Moderne JavaScript Syntax
Lerne die wichtigsten ES6+ Features: Destructuring, Spread/Rest, Arrow Functions, Template Literals, Optional Chaining und mehr.
JavaScript Fetch API & AJAX - HTTP Requests einfach gemacht
Lerne wie du mit der Fetch API HTTP Requests sendest, Daten von APIs lädst und mit dem Server kommunizierst. Inklusive Error Handling und praktischen Beispielen.