Node.js Event Loop für Anfänger erklärt

Backend2026-09-14TryQuickToolBox

Du hast wahrscheinlich schon gehört, dass Node.js single-threaded ist und einen Event Loop verwendet, um Tausende gleichzeitiger Verbindungen zu verarbeiten. Aber was bedeutet das eigentlich? Wenn du neu bei Node.js bist, kann der Event Loop wie eine mysteriöse Blackbox erscheinen. Dieser Artikel erklärt ihn in einfacher Sprache, damit du besseren, schnelleren Node.js-Code schreiben kannst.

Warum der Event Loop existiert

Traditionelle Webserver (wie Apache mit PHP) erstellen für jede Anfrage einen neuen Thread oder Prozess. Das funktioniert, skaliert aber nicht gut, wenn du viele gleichzeitige Verbindungen hast – jeder Thread verbraucht Speicher und CPU. Node.js verfolgt einen anderen Ansatz: Es führt dein JavaScript in einem einzigen Thread aus und verwendet einen Event Loop, um I/O-Operationen asynchron zu verarbeiten. Das bedeutet, dass dein Code nicht auf das Ende einer Datenbankabfrage oder eines Dateilesevorgangs wartet; er führt weiter andere Aufgaben aus und kommt zurück, wenn das Ergebnis bereit ist.

Die Kernkomponenten

Bevor wir in den Event Loop eintauchen, klären wir die Hauptakteure:

Wie der Event Loop funktioniert: Ein einfaches Beispiel

Betrachte diesen Code:

console.log('Start');

setTimeout(() => {
  console.log('Timeout');
}, 0);

console.log('End');

Was ist die Ausgabe? Wenn du Start, End, Timeout geraten hast, hast du recht. Obwohl der Timeout 0 Millisekunden beträgt, wird der Callback nicht sofort ausgeführt. Hier ist, was passiert:

  1. console.log('Start') wird auf den Call Stack gepusht, ausgeführt und gepoppt.
  2. setTimeout wird aufgerufen. Node.js registriert den Timer und setzt ihn auf Ablauf nach 0ms. Der Callback wird gespeichert, und die Funktion kehrt zurück.
  3. console.log('End') wird gepusht, ausgeführt und gepoppt.
  4. Jetzt ist der Call Stack leer. Der Event Loop prüft die Timer-Phase und sieht, dass der Timer abgelaufen ist. Er verschiebt den Callback auf den Call Stack, der Timeout ausgibt.

Dies demonstriert die nicht-blockierende Natur: Der Timer blockiert nicht den Rest des Codes.

Die Phasen des Event Loops

Der Event Loop verarbeitet Callbacks in mehreren Phasen, jede mit einem bestimmten Zweck. Das Verständnis dieser Phasen hilft dir, die Ausführungsreihenfolge vorherzusagen.

Phase Beschreibung
Timers Führt Callbacks aus, die von setTimeout() und setInterval() geplant wurden.
Pending Callbacks Führt I/O-Callbacks aus, die auf die nächste Loop-Iteration verschoben wurden.
Idle, Prepare Wird intern von Node.js verwendet.
Poll Ruft neue I/O-Ereignisse ab; führt I/O-bezogene Callbacks aus (fast alle außer Timers, setImmediate und Close-Callbacks).
Check Führt Callbacks aus, die von setImmediate() geplant wurden.
Close Callbacks Führt Close-Callbacks aus, z. B. socket.on('close', ...).

Zwischen jeder Phase prüft Node.js auf Microtasks: process.nextTick()-Callbacks und Promises. Diese haben höhere Priorität und werden sofort nach Abschluss der aktuellen Operation ausgeführt, bevor zur nächsten Phase übergegangen wird.

Microtasks: nextTick und Promises

Microtasks sind nicht Teil der Event-Loop-Phasen; sie werden nach jeder Phase und nach jedem Callback verarbeitet. Dadurch laufen sie vor Timern und I/O-Callbacks. Zum Beispiel:

setTimeout(() => console.log('Timeout'), 0);
Promise.resolve().then(() => console.log('Promise'));
process.nextTick(() => console.log('nextTick'));
console.log('Sync');

Ausgabe: Sync, nextTick, Promise, Timeout. Der synchrone Code läuft zuerst, dann Microtasks (nextTick vor Promises), dann der Timer.

Warnung: Rekursive process.nextTick()-Aufrufe können den Event Loop aushungern und I/O verhindern. Verwende setImmediate() für rekursive Operationen.

setImmediate vs setTimeout

setImmediate() ist darauf ausgelegt, einen Callback sofort nach Abschluss der aktuellen Poll-Phase auszuführen. Im Gegensatz dazu wartet setTimeout() mit 0ms auf die nächste Timer-Phase. Die Reihenfolge zwischen ihnen kann je nach Kontext variieren, aber innerhalb eines I/O-Callbacks läuft setImmediate() immer zuerst.

Warum das für deinen Code wichtig ist

Das Verständnis des Event Loops hilft dir, häufige Fallstricke zu vermeiden:

Praktisches Beispiel: Nicht-blockierendes Dateilesen

Hier ist, wie der Event Loop nicht-blockierende I/O ermöglicht:

const fs = require('fs');

console.log('Before read');

fs.readFile('large-file.txt', 'utf8', (err, data) => {
  if (err) throw err;
  console.log('File read complete');
});

console.log('After read');

Ausgabe: Before read, After read, dann File read complete. Das Dateilesen erfolgt im Hintergrund, und der Callback wird in die Warteschlange gestellt, wenn es fertig ist. In der Zwischenzeit kann anderer Code ausgeführt werden.

FAQ

Ist Node.js wirklich single-threaded?

Node.js führt dein JavaScript in einem einzigen Thread aus, verwendet aber mehrere Threads im libuv-Thread-Pool für Datei-I/O, DNS und andere Operationen. Unter der Haube ist es also nicht vollständig single-threaded.

Was ist der Unterschied zwischen dem Call Stack und dem Event Loop?

Der Call Stack führt synchronen Code aus. Der Event Loop verwaltet asynchrone Callbacks und verschiebt sie aus den Warteschlangen auf den Call Stack, wenn dieser leer ist.

Kann ich mehrere Event Loops erstellen?

Nein, jeder Node.js-Prozess hat einen Event Loop. Du kannst jedoch Worker-Threads verwenden, um separate JavaScript-Threads auszuführen, jeder mit seinem eigenen Event Loop.

Wenn du mit Server-Logs arbeitest, um Performance-Probleme zu debuggen, probiere unseren Nginx Log Analyzer, um Log-Daten schnell zu parsen und zu visualisieren.