Async Await Promises JavaScript
Async/Await & Promises: Asynchrones JavaScript
Asynchroner Code ist das Herzstück von JavaScript. Verstehen Sie Promises und async/await für sauberen, lesbaren Code.
Die Evolution
// 1. Callbacks (Callback Hell)
getData(function(a) {
getMoreData(a, function(b) {
getEvenMoreData(b, function(c) {
console.log(c);
});
});
});
// 2. Promises (besser)
getData()
.then(a => getMoreData(a))
.then(b => getEvenMoreData(b))
.then(c => console.log(c))
.catch(err => console.error(err));
// 3. Async/Await (am besten)
async function fetchAll() {
const a = await getData();
const b = await getMoreData(a);
const c = await getEvenMoreData(b);
console.log(c);
}
Promises erstellen
// Promise erstellen
function fetchUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) {
resolve({ id, name: 'Max' });
} else {
reject(new Error('Invalid ID'));
}
}, 1000);
});
}
// Promise verwenden
fetchUser(1)
.then(user => console.log(user))
.catch(err => console.error(err))
.finally(() => console.log('Done'));
// Promise States:
// - pending: Noch nicht abgeschlossen
// - fulfilled: Erfolgreich (resolve)
// - rejected: Fehlgeschlagen (reject)
Async/Await Grundlagen
// Async Funktion gibt immer Promise zurück
async function getUser(id) {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error('User not found');
}
return response.json(); // Automatisch Promise
}
// Verwendung
async function main() {
try {
const user = await getUser(1);
console.log(user);
} catch (error) {
console.error(error);
}
}
// Oder mit .catch()
getUser(1)
.then(user => console.log(user))
.catch(err => console.error(err));
Fehlerbehandlung
// Try/Catch (empfohlen für async/await)
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Fetch failed:', error);
throw error; // Weitergeben oder behandeln
}
}
// Wrapper-Funktion für sauberes Error Handling
async function safeAsync(promise) {
try {
const data = await promise;
return [data, null];
} catch (error) {
return [null, error];
}
}
// Verwendung
const [user, error] = await safeAsync(getUser(1));
if (error) {
console.error('Error:', error);
} else {
console.log('User:', user);
}
Parallele Ausführung
// ❌ Sequentiell (langsam)
async function fetchSequential() {
const user = await fetchUser(1); // 1 Sekunde
const posts = await fetchPosts(1); // 1 Sekunde
const comments = await fetchComments(); // 1 Sekunde
// Total: 3 Sekunden
}
// ✅ Parallel (schnell)
async function fetchParallel() {
const [user, posts, comments] = await Promise.all([
fetchUser(1),
fetchPosts(1),
fetchComments()
]);
// Total: ~1 Sekunde (längste Request)
}
// Promise.all schlägt fehl wenn EINER fehlschlägt
// Promise.allSettled wartet auf alle (auch bei Fehlern)
const results = await Promise.allSettled([
fetchUser(1),
fetchUser(999) // Fehler
]);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Success ${index}:`, result.value);
} else {
console.log(`Failed ${index}:`, result.reason);
}
});
Race Conditions
// Promise.race: Erster gewinnt
const fastest = await Promise.race([
fetch('/api/server1/data'),
fetch('/api/server2/data')
]);
// Timeout implementieren
function fetchWithTimeout(url, timeout = 5000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
)
]);
}
const data = await fetchWithTimeout('/api/slow-endpoint', 3000);
Async Iteration
// Sequentiell durch Array
async function processSequentially(items) {
for (const item of items) {
await processItem(item);
}
}
// Parallel durch Array (alle gleichzeitig)
async function processParallel(items) {
await Promise.all(items.map(item => processItem(item)));
}
// Parallel mit Limit (z.B. max 3 gleichzeitig)
async function processWithLimit(items, limit = 3) {
const results = [];
const executing = [];
for (const item of items) {
const promise = processItem(item).then(result => {
executing.splice(executing.indexOf(promise), 1);
return result;
});
results.push(promise);
executing.push(promise);
if (executing.length >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
Async in Schleifen
const ids = [1, 2, 3, 4, 5];
// ❌ forEach funktioniert NICHT mit await
ids.forEach(async (id) => {
const user = await fetchUser(id); // Wartet nicht!
console.log(user);
});
// ✅ for...of funktioniert
for (const id of ids) {
const user = await fetchUser(id); // Sequentiell
console.log(user);
}
// ✅ map + Promise.all für parallel
const users = await Promise.all(
ids.map(id => fetchUser(id))
);
Praktisches Beispiel
class ApiClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
}
async request(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
try {
const response = await fetch(url, {
headers: { 'Content-Type': 'application/json' },
...options
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
} catch (error) {
console.error(`API Error: ${endpoint}`, error);
throw error;
}
}
async get(endpoint) {
return this.request(endpoint);
}
async post(endpoint, data) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(data)
});
}
}
// Verwendung
const api = new ApiClient('https://api.example.com');
const users = await api.get('/users');
const newUser = await api.post('/users', { name: 'Max' });
💡 Tipp:
Verwenden Sie immer try/catch bei async/await. Unbehandelte Promise-Rejections können zu schwer zu findenden Bugs führen.