Async/await und Array.map in TypeScript: 5 tödliche Fehler

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Async/await und Array.map: Wenn beste Freunde zu Feinden werden

\n\n

Haben Sie jemals so etwas wie const results = await array.map(async (item) => {...}) geschrieben und ein Array von Promise anstelle der erwarteten Daten erhalten? Wenn ja – Sie sind nicht allein. Diese Frage auf Stack Overflow hat zehntausende Aufrufe gesammelt, und die Anzahl der Entwickler, die in diese Falle getappt sind, geht in die Millionen.

\n\n

In diesem Artikel analysieren wir die 5 gefährlichsten Fehler bei der Verwendung von async/await mit Array.map in TypeScript. Und, was noch wichtiger ist: Ich zeige Ihnen, wie Sie sie vermeiden. Schnallen Sie sich an – es wird schmerzhaft, aber lehrreich.

\n\n

Fehler Nr. 1: Promise.all vergessen (der häufigste)

\n\n

Der häufigste Bug ist die Annahme, dass await innerhalb von map auf magische Weise auf alle Promises wartet. Spoiler: Tut es nicht.

\n\n

Problem

\n
// ❌ So funktioniert es nicht\nconst users = [1, 2, 3, 4, 5];\nconst userData = await users.map(async (id) => {\n  const response = await fetch(`/api/users/${id}`);\n  return response.json();\n});\n// userData ist Promise[], nicht UserData[]!\nconsole.log(userData); // [Promise, Promise, Promise, Promise, Promise]
\n\n

Warum passiert das?

\n

Array.map ist synchron. Es wartet nicht auf den Abschluss asynchroner Callbacks. Jeder Aufruf einer async-Funktion gibt ein Promise zurück, und map sammelt sie einfach in einem Array. Das await vor map wartet nur auf den Abschluss von map selbst, nicht aber auf dessen Callbacks.

\n\n

Lösung

\n
// ✅ Richtig: In Promise.all einwickeln\nconst users = [1, 2, 3, 4, 5];\nconst userData = await Promise.all(\n  users.map(async (id) => {\n    const response = await fetch(`/api/users/${id}`);\n    return response.json();\n  })\n);\n// userData ist UserData[]! 🎉\nconsole.log(userData);
\n\n

Fehler Nr. 2: Alle Promises werden parallel ausgeführt (sequentiell wäre nötig)

\n\n

Promise.all startet alle Promises parallel. Das ist toll für die Leistung, aber manchmal müssen wir Anfragen strikt nacheinander ausführen. Zum Beispiel bei der Arbeit mit einer API, die die Anzahl der Anfragen begrenzt, oder wenn jede nächste Anfrage von der vorherigen abhängt.

\n\n

Problem

\n
// ❌ Alle Anfragen fliegen gleichzeitig raus\nconst ids = [1, 2, 3, 4, 5];\nconst results = await Promise.all(\n  ids.map(async (id) => {\n    // 5 Anfragen an die API gleichzeitig – kann zu Rate Limit führen\n    return await api.processItem(id);\n  })\n);
\n\n

Lösung: Sequentielle Ausführung

\n
// ✅ for...of mit await verwenden\nconst ids = [1, 2, 3, 4, 5];\nconst results: ItemData[] = [];\n\nfor (const id of ids) {\n  const result = await api.processItem(id);\n  results.push(result);\n}\n// results – Array mit Daten, der Reihe nach verarbeitet
\n\n

Alternative – Array.reduce mit einem Promise-Akkumulator:

\n
// ✅ Funktioniert auch, ist aber weniger lesbar\nconst ids = [1, 2, 3, 4, 5];\nconst results = await ids.reduce(async (accPromise, id) => {\n  const acc = await accPromise;\n  const result = await api.processItem(id);\n  return [...acc, result];\n}, Promise.resolve([] as ItemData[]));
\n\n

Fehler Nr. 3: Falsche Typisierung in TypeScript

\n\n

TypeScript versteht möglicherweise nicht sofort, was Sie mit einer asynchronen map machen. Besonders wenn Sie Promise.all ohne explizite Typangabe verwenden.

\n\n

Problem

\n
// ❌ TypeScript meckert oder leitet den falschen Typ ab\nconst ids = [1, 2, 3];\nconst results = await Promise.all(\n  ids.map(async (id) => {\n    const data = await fetchData(id);\n    return data.name; // TypeScript könnte denken, das sei Promise<string>\n  })\n);\n// results hat den Typ unknown oder Promise<string>[]
\n\n

Lösung: Explizite Typannotation

\n
// ✅ Den Ergebnistyp explizit angeben                

Blogs

Buchempfehlungen