{}const=>[]async()letfn</>var
EntwicklungGrundlagenJS

Async/Await: Die ultimative Anleitung für asynchronen Code

Wir verstehen, wie man mit asynchronem Code mit Hilfe von async/await arbeitet. Erfahren Sie, wie Sie Callback-Hölle vermeiden, Fehler richtig behandeln, parallele Ausführung von Operationen optimieren und erweiterte Muster anwenden können.

К

Kodik

Autor

6 Min. Lesezeit

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.

🔥 100.000+ Schüler sind bereits bei uns

Genug Theorie gelesen?
Zeit zu coden!

Kodik — eine App, in der du durch Praxis programmieren lernst. KI-Mentor, interaktive Lektionen, echte Projekte.

🤖 KI 24/7
🎓 Zertifikate
💰 Kostenlos
🚀 Jetzt starten
Heute beigetreten

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:

  • await kann nur innerhalb von async Funktionen verwendet werden

  • async Funktion gibt immer Promis zurück

  • Wenn 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ät

Erweiterte 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!

🎯Hör auf zu zögern

Artikel gefallen?
Zeit zum Üben!

Bei Kodik liest du nicht nur — du schreibst sofort Code. Theorie + Praxis = echte Skills.

Sofortige Praxis
🧠KI erklärt Code
🏆Zertifikat

Keine Registrierung • Keine Karte