Wie JavaScript Promises mit der Event Loop funktionieren
  Das asynchrone Verhalten von JavaScript verwirrt oft Entwickler. Sie schreiben setTimeout(() => console.log('timeout'), 0) und erwarten eine sofortige Ausführung, doch ein Promise wird zuerst aufgelöst. Das Verständnis, wie JavaScript Promises mit der Event Loop interagieren, erklärt, warum dies geschieht, und hilft Ihnen, vorhersehbareren asynchronen Code zu schreiben.
Wichtigste Erkenntnisse
- Die Event Loop verarbeitet alle Microtasks, bevor sie zum nächsten Macrotask übergeht
 - Promises und async/await nutzen die Microtask-Warteschlange und erhalten Vorrang vor setTimeout
 - Das Verständnis des Zwei-Warteschlangen-Systems hilft, die Ausführungsreihenfolge von JavaScript vorherzusagen
 - Rekursive Microtasks können die Event Loop aushungern und Macrotasks blockieren
 
Die Grundlagen der JavaScript Event Loop
JavaScript läuft auf einem einzelnen Thread und verarbeitet jeweils eine Operation. Die Event Loop ermöglicht asynchrone Operationen, indem sie zwischen dem Call Stack und zwei verschiedenen Warteschlangen koordiniert: der Microtask-Warteschlange und der Macrotask-Warteschlange.
Der Call Stack führt synchronen Code sofort aus. Wenn der Stack leer ist, prüft die Event Loop in einer bestimmten Reihenfolge auf ausstehende Aufgaben:
- Alle Microtasks werden zuerst ausgeführt
 - Ein Macrotask wird ausgeführt
 - Der Zyklus wiederholt sich
 
Dieses Prioritätssystem erklärt, warum sich Promises anders verhalten als setTimeout.
JavaScript Macrotask vs. Microtask: Der entscheidende Unterschied
Das Verständnis der Unterschiede zwischen Macrotask und Microtask ist entscheidend für die Vorhersage der Code-Ausführungsreihenfolge.
Zu den Macrotasks gehören:
setTimeoutsetInterval- I/O-Operationen
 - UI-Rendering
 
Zu den Microtasks gehören:
- Promise-Callbacks (
.then,.catch,.finally) queueMicrotask()- MutationObserver-Callbacks
 
Die Event Loop verarbeitet alle Microtasks, bevor sie zum nächsten Macrotask übergeht. Dies schafft ein Prioritätssystem, bei dem Promises immer vor Timern ausgeführt werden.
Promises und die Microtask-Warteschlange in Aktion
Betrachten wir anhand von Code, wie Promises mit der Event Loop interagieren:
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve()
  .then(() => console.log('3'))
  .then(() => console.log('4'));
console.log('5');
Ausgabe: 1, 5, 3, 4, 2
Hier ist der Ausführungsablauf:
- Synchrones 
console.log('1')wird sofort ausgeführt setTimeoutplant Callback in die Macrotask-Warteschlange ein- Promise-Callbacks werden als Microtasks in die Warteschlange eingereiht
 - Synchrones 
console.log('5')wird ausgeführt - Event Loop verarbeitet alle Microtasks (3, 4)
 - Event Loop verarbeitet einen Macrotask (2)
 
Die Microtask-Warteschlange wird vollständig geleert, bevor ein Macrotask ausgeführt wird, selbst bei Timeouts mit Nullverzögerung.
Discover how at OpenReplay.com.
Async/Await und die Integration in die Event Loop
Das Verhalten von Async/Await folgt denselben Microtask-Regeln. Das Schlüsselwort await pausiert die Funktionsausführung und plant die Fortsetzung als Microtask ein:
async function example() {
  console.log('1');
  await Promise.resolve();
  console.log('2');  // Dies wird zu einem Microtask
}
example();
console.log('3');
// Ausgabe: 1, 3, 2
Nach await wird der verbleibende Funktionskörper der Microtask-Warteschlange hinzugefügt. Dies erklärt, warum console.log('3') vor console.log('2') ausgeführt wird, obwohl es später im Code erscheint.
Häufige Fallstricke und praktische Muster
Aushungern der Microtask-Warteschlange
Das rekursive Erstellen von Microtasks kann die Event Loop blockieren:
function dangerousLoop() {
  Promise.resolve().then(dangerousLoop);
}
// Tun Sie dies nicht - blockiert alle Macrotasks
Kombination von Timern mit Promises
Wenn Sie verschiedene asynchrone Muster kombinieren, denken Sie an die Ausführungspriorität:
setTimeout(() => console.log('timeout'), 0);
fetch('/api/data')
  .then(response => response.json())
  .then(data => console.log('data'));
Promise.resolve().then(() => console.log('immediate'));
// Reihenfolge: immediate → data (wenn bereit) → timeout
Debugging der Ausführungsreihenfolge
Verwenden Sie dieses Muster, um den Ausführungsablauf zu verfolgen:
console.log('Sync start');
queueMicrotask(() => console.log('Microtask 1'));
setTimeout(() => console.log('Macrotask 1'), 0);
Promise.resolve()
  .then(() => console.log('Microtask 2'))
  .then(() => console.log('Microtask 3'));
setTimeout(() => console.log('Macrotask 2'), 0);
console.log('Sync end');
// Ausgabe: Sync start, Sync end, Microtask 1, Microtask 2, Microtask 3, Macrotask 1, Macrotask 2
Fazit
Das Zwei-Warteschlangen-System der Event Loop bestimmt die asynchrone Ausführungsreihenfolge von JavaScript. Promises und async/await nutzen die Microtask-Warteschlange und erhalten Vorrang vor setTimeout und anderen Macrotasks. Dieses Wissen verwandelt mysteriöses asynchrones Verhalten in vorhersehbare Muster und ermöglicht es Ihnen, zuverlässigeren asynchronen JavaScript-Code zu schreiben.
Merken Sie sich: Synchroner Code wird zuerst ausgeführt, dann werden alle Microtasks abgearbeitet, dann wird ein Macrotask ausgeführt. Dieser Zyklus wiederholt sich und ermöglicht es dem einzelnen Thread von JavaScript, komplexe asynchrone Operationen effizient zu verarbeiten.
Häufig gestellte Fragen (FAQs)
Promise-Callbacks sind Microtasks, während setTimeout Macrotasks erstellt. Die Event Loop verarbeitet immer alle Microtasks, bevor sie zum nächsten Macrotask übergeht, unabhängig von der Timeout-Dauer.
Ja, das kontinuierliche Erstellen von Promises oder Microtasks ohne Ausführung von Macrotasks kann die Event Loop aushungern. Dies verhindert, dass UI-Updates und andere Macrotasks ausgeführt werden.
Async/await ist syntaktischer Zucker für Promises. Das Schlüsselwort await pausiert die Ausführung und plant die Fortsetzung als Microtask ein, wobei dieselben Prioritätsregeln wie bei Promise-Callbacks befolgt werden.
Verschiedene Muster folgen ihren Warteschlangenregeln. Promises und async/await verwenden Microtasks, während setTimeout und setInterval Macrotasks verwenden. Microtasks werden immer zuerst ausgeführt, wenn der Call Stack leer ist.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.