Warum brauchen wir Asynchronität?
Stellen Sie sich vor, Ihre Anwendung stellt eine Anfrage an eine externe API, die 2 Sekunden lang antwortet. Ohne Asynchronität hängt das Programm während dieser Zeit einfach und blockiert die Ausführung anderer Operationen. Mit asynchronem Code kann die Anwendung weiter ausgeführt werden, während der Vorgang im Hintergrund ausgeführt wird.
Von Callbacks zu Promises und Async/Await
Die Entwicklung des asynchronen Codes in JavaScript hat mehrere Phasen durchlaufen:
Callback-Funktionen (veralteter Ansatz):
fetchUser(userId, (error, user) => {
if (error) {
console.error(error);
return;
}
fetchPosts(user.id, (error, posts) => {
if (error) {
console.error(error);
return;
}
// Callback hell beginnt hier
});
});Versprechen (Verbesserung):
fetchUser(userId)
.then(user => fetchPosts(user.id))
.then(posts => console.log(posts))
.catch(error => console.error(error));Async/Await (moderner Ansatz):
async function getUserPosts(userId) {
try {
const user = await fetchUser(userId);
const posts = await fetchPosts(user.id);
return posts;
} catch (error) {
console.error(error);
}
}Grundlagen von async/await
Das Schlüsselwort async vor der Funktion bedeutet, dass die Funktion immer ein Versprechen zurückgibt. Das Schlüsselwort await lässt JavaScript auf das Ergebnis des Versprechens warten, bevor es mit der Ausführung fortfährt.
async function fetchData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
}Wichtige Regeln:
awaitkann nur innerhalb vonasyncFunktionen verwendet werdenasyncFunktion gibt immer Promis zurückWenn die Funktion einen Wert zurückgibt, wird dieser automatisch in
Promise.resolve()umgewandelt
Fehlerbehandlung
Try-catch-Blöcke sind eine natürliche Möglichkeit, Fehler in async/await zu behandeln:
async function processOrder(orderId) {
try {
const order = await fetchOrder(orderId);
const payment = await processPayment(order);
const confirmation = await sendConfirmation(payment);
return confirmation;
} catch (error) {
if (error.code === 'PAYMENT_FAILED') {
await refundOrder(orderId);
}
throw new Error(`Order processing failed: ${error.message}`);
}
}Kann mit .catch() für eine detailliertere Verarbeitung kombiniert werden:
async function getData() {
const data = await fetchData().catch(error => {
console.error('Fetch failed:', error);
return getDefaultData(); // Standarddaten werden wiederhergestellt
});
return data;
}
Parallelisierung
Einer der häufigsten Fehler ist die sequentielle Ausführung unabhängiger Operationen:
// Schlecht: Operationen werden nacheinander ausgeführt (6 Sekunden)
async function loadData() {
const users = await fetchUsers(); // 3 Sekunden
const posts = await fetchPosts(); // 3 Sekunden
return { users, posts };
}Verwenden Sie Promise.all() für die parallele Ausführung:
// Gut: Operationen werden parallel ausgeführt (3 Sekunden)
async function loadData() {
const [users, posts] = await Promise.all([
fetchUsers(),
fetchPosts()
]);
return { users, posts };
}Promise.allSettled()
Für Fälle, in denen die Ergebnisse aller Operationen benötigt werden:
async function loadAllData() {
const results = await Promise.allSettled([
fetchUsers(),
fetchPosts(),
fetchComments()
]);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Operation ${index} succeeded:`, result.value);
} else {
console.error(`Operation ${index} failed:`, result.reason);
}
});
}Promise.race()
Für Timeout-Operationen:
async function fetchWithTimeout(url, timeout = 5000) {
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Request timeout')), timeout)
);
return Promise.race([
fetch(url),
timeoutPromise
]);
}Async/await in Schleifen
Seien Sie vorsichtig mit Zyklen — sie können unerwartetes Verhalten verursachen:
// Serielle Verarbeitung
async function processSequentially(items) {
for (const item of items) {
await processItem(item); // Wir warten auf den Abschluss jeder Operation
}
}
// Parallelverarbeitung
async function processInParallel(items) {
await Promise.all(items.map(item => processItem(item)));
}
// Batch-Verarbeitung
async function processBatches(items, batchSize = 5) {
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
await Promise.all(batch.map(item => processItem(item)));
}
}Häufige Fehler
1. Vergessene await
// Fehler: Die Funktion gibt Promis zurück, keine Daten
async function getData() {
return fetchData(); // Sie haben vergessen, zu warten
}
// Richtig
async function getData() {
return await fetchData();
}2. Verwendung von await in forEach
// Funktioniert nicht: forEach versteht async nicht
items.forEach(async (item) => {
await processItem(item);
});
// Verwenden Sie for...of
for (const item of items) {
await processItem(item);
}3. Erstellen von Promis im Zyklus ohne Kontrolle
// Erstellt Tausende von gleichzeitigen Anfragen
const promises = items.map(item => fetchItem(item));
await Promise.all(promises);
// Bessere Kontrolle der ParallelitätErweiterte Muster
Wiederholen mit exponentieller Verzögerung:
async function fetchWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fetch(url);
} catch (error) {
if (i === maxRetries - 1) throw error;
const delay = Math.pow(2, i) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}Zwischenspeichern von Ergebnissen:
const cache = new Map();
async function fetchWithCache(key, fetcher) {
if (cache.has(key)) {
return cache.get(key);
}
const result = await fetcher();
cache.set(key, result);
return result;
}Debounce für Async-Funktionen:
function asyncDebounce(func, wait) {
let timeout;
return function(...args) {
return new Promise((resolve) => {
clearTimeout(timeout);
timeout = setTimeout(async () => {
resolve(await func.apply(this, args));
}, wait);
});
};
}Async/await in anderen Sprachen
Das Konzept von async/await existiert nicht nur in JavaScript:
Python:
async def fetch_data():
response = await aiohttp.get('https://api.example.com')
return await response.json()C#:
async Task<string> FetchDataAsync() {
var response = await httpClient.GetAsync("https://api.example.com");
return await response.Content.ReadAsStringAsync();
}Rust:
async fn fetch_data() -> Result<String, Error> {
let response = reqwest::get("https://api.example.com").await?;
Ok(response.text().await?)
}Leistung und Best Practices
1. Vermeiden Sie übermäßiges Warten
// Nicht optimal
async function process() {
const result = await computeValue();
return result; // Unnötiges Warten
}
// Besser
async function process() {
return computeValue();
}2. Verwenden Sie Parallelität, wo immer möglich
// Langsam
const user = await getUser();
const posts = await getPosts();
// Schneller
const [user, posts] = await Promise.all([getUser(), getPosts()]);3. Fügen Sie Timeouts für externe Anfragen hinzu
async function safeFetch(url) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
try {
const response = await fetch(url, { signal: controller.signal });
return response;
} finally {
clearTimeout(timeout);
}
}Befund
Async/await macht asynchronen Code lesbar und verständlich, beseitigt Callback-Hell-Probleme und vereinfacht die Fehlerbehandlung. Wichtigste Punkte:
Verwenden Sie async/await für sequentielle Operationen
Verwenden Sie Promise.all() für die parallele Ausführung
Vergessen Sie nicht, Fehler über try-catch zu behandeln
Seien Sie vorsichtig mit Zyklen und verwenden Sie immer await wo nötig
Überprüfen Sie die Parallelität, um eine Überlastung des Systems zu vermeiden
Die Praxis und das Verständnis, wann Operationen nacheinander und wann parallel ausgeführt werden sollten, hilft Ihnen, effektiven asynchronen Code zu schreiben.
Kodik bietet strukturierte Kurse in JavaScript, Python, C, Rust und vielen anderen Sprachen, in denen Sie asynchrone Programmierung von den Grundlagen bis zu fortgeschrittenen Mustern erlernen können. Interaktive Lektionen mit praktischen Aufgaben helfen, das Material anhand von realen Beispielen zu festigen.
Haben Sie Fragen zu async/await oder sind Sie auf eine schwierige Aufgabe gestoßen? Schließen Sie sich uns an unseren Telegram-Kanal, wo erfahrene Entwickler und eine aktive Community immer bereit sind, bei jedem Problem zu helfen – von syntaktischen Feinheiten bis hin zu architektonischen Lösungen. Stellen Sie Fragen, teilen Sie Erfahrungen und lernen Sie mit der Community!
