Node.js Internals

Node.js Event Loop

Node führt Ihr JavaScript in einem einzigen Thread aus, bewältigt aber dennoch tausende von Verbindungen. Der Event Loop und seine Phasen sind der Grund, warum das funktioniert – und warum das Blockieren des Loops problematisch ist.

intermediate15 min readUpdated 15. Sept. 2026
order.js
js
// order.js
console.log("1: synchronous");

setTimeout(() => console.log("5: timers phase"), 0);
setImmediate(() => console.log("6: check phase"));

Promise.resolve().then(() => console.log("3: microtask"));

process.nextTick(() => console.log("2: nextTick"));
Threads
Ein Haupt-Thread
Engine
V8 plus libuv
Phasen
Timers, poll, check, close
Microtasks
nextTick und Promises
Thread Pool
Standardgröße 4
Blocking
Stoppt alles

Warum es wichtig ist

Warum der Event Loop wichtig ist

Ein Thread, viele Verbindungen

Der Loop multiplexed tausende von Sockets auf einem einzigen Thread, indem er niemals synchron wartet.

Vorhersehbare Reihenfolge

Die Kenntnis der Phasen und Queues ermöglicht es Ihnen, nachzuvollziehen, wann Callbacks ausgeführt werden.

Blocking ist der Feind

Jede lange synchrone Aufgabe friert jede andere Anfrage ein, weshalb rechenintensive Arbeit aus dem Loop ausgelagert wird.

Das Gesamtbild

Die drei Teile des Loops

Phasen entscheiden, was wann ausgeführt wird, Microtasks laufen zwischen den Phasen, und der Thread Pool übernimmt Aufgaben, die nicht non-blocking sein können.

Phasen

Planung

Der Loop durchläuft Timer, ausstehende Callbacks, Poll, Check und Close-Callbacks.

Queues

Priorisierung

Microtasks werden zwischen den Phasen abgearbeitet, wobei nextTick vor Promise-Callbacks ausgeführt wird.

Thread Pool

Auslagerung

Datei-, DNS- und Krypto-Aufgaben laufen im Thread Pool von libuv, damit der Loop frei bleibt.

Der Event Loop auf einen Blick

Die Kernkonzepte

Timers-Phase

Führt Callbacks aus, die durch setTimeout und setInterval geplant wurden und deren Zeit abgelaufen ist.

Poll-Phase

Ruft neue I/O-Events ab und führt deren Callbacks aus; blockiert im Leerlauf.

Check-Phase

Führt setImmediate-Callbacks direkt nach der Poll-Phase aus.

Microtasks

process.nextTick und Promise-Callbacks werden zwischen den Phasen ausgeführt.

Thread Pool

libuv führt fs, DNS und einige Krypto-Operationen auf Worker-Threads aus.

Monitoring

Überwachen Sie den Loop-Delay und die Event Loop Utilization, um Blocking zu erkennen.

Eine kurze Geschichte

Von blockierenden Servern zu event-gesteuertem I/O

  1. 2009

    Event-gesteuertes Node

    Node kombiniert V8 mit libuv, um JavaScript mit non-blocking I/O auszuführen.

    09
  2. 2011

    libuv wird ausgegliedert

    Der Event Loop und die Plattform-Abstraktionen werden zu einer eigenständigen Bibliothek.

    11
  3. 2015

    Promises und Microtasks

    Native Promises fügen eine neue Queue mit klaren Reihenfolgeregeln hinzu.

    15
  4. 2020

    Bessere Diagnostik

    Tools wie die Event Loop Utilization machen Blocking messbar.

    20
  5. Heute

    Das gleiche Modell überall

    Der Event Loop bildet die Grundlage für Server, Tools, Serverless-Runtimes und sogar Browser.

    Heute

Der vollständige Leitfaden

Node.js Event Loop: Alles was Sie wissen müssen

Warum der Event Loop wichtig ist

Node.js führt dein JavaScript auf einem einzigen Main Thread aus, dennoch kann ein einziger Server tausende gleichzeitige Verbindungen verarbeiten. Das ist möglich, weil der Event Loop niemals wartet. Wenn eine Operation blockierend wäre, übergibt Node diese an das Betriebssystem oder an einen Thread Pool und macht mit anderen Aufgaben weiter. Sobald das Ergebnis bereitsteht, wird ein Callback in die Queue eingereiht und der Loop führt ihn aus.

Das Verständnis des Loops erklärt vieles am Verhalten von Node: warum ein synchrones Dateilesen den gesamten Server einfriert, warum ein Promise vor einem Timer mit einer Verzögerung von Null geloggt wird und warum CPU-intensive Arbeit einen Worker benötigt. Es ist das wichtigste mentale Modell, um performanten Node-Code zu schreiben.

Der Loop und seine Phasen

Der Event Loop ist ein Zyklus aus verschiedenen Phasen. Jede Phase besitzt eine Queue von Callbacks, die der Loop abarbeitet, bevor er zur nächsten Phase übergeht.

  1. Timers — fällige Callbacks, die über setTimeout und setInterval geplant wurden.
  2. Pending callbacks — aufgeschobene System-Callbacks, wie zum Beispiel bestimmte TCP-Fehler.
  3. Idle, prepare — interne Buchführung.
  4. Poll — ruft neue I/O-Events ab und führt deren Callbacks aus. Wenn nichts bereitsteht, kann der Loop hier kurzzeitig blockieren.
  5. Check — führt setImmediate-Callbacks aus.
  6. Close callbacks — verarbeitet Events wie das Schließen von Sockets.

Der Loop läuft so lange weiter, wie es Arbeit zu erledigen gibt. Wenn keine Timer mehr aktiv sind, kein ausstehender I/O vorliegt und keine Handles mehr existieren, beendet sich der Prozess.

Microtasks: nextTick und Promises

Zwischen den Phasen und nach jedem Callback leert Node zwei hochpriorisierte Queues:

  • process.nextTick Callbacks, die zuerst ausgeführt werden.
  • Promise Callbacks, die nach nextTick ausgeführt werden.

Beide werden vollständig geleert, bevor der Loop fortfährt, was zwei Konsequenzen hat. Erstens werden sie vor Timern und I/O-Callbacks ausgeführt, die bereits früher geplant wurden. Zweitens kann ein rekursives nextTick oder eine endlose Promise-Kette den Loop blockieren (starve the loop), sodass I/O-Operationen niemals ausgeführt werden.

// order.js
console.log("1: synchronous");

setTimeout(() => console.log("5: timers"), 0);
setImmediate(() => console.log("6: check"));

Promise.resolve().then(() => console.log("3: promise"));
process.nextTick(() => console.log("2: nextTick"));

Die Ausgabe ist 1, 2, 3, dann 5 und 6. Die Reihenfolge von 5 und 6 kann auf der obersten Ebene variieren, da sie davon abhängt, wie lange es dauert, die Timer-Phase zu erreichen; innerhalb eines I/O-Callbacks wird setImmediate immer vor setTimeout ausgeführt.

Der libuv Thread Pool

Einige Operationen können vom Betriebssystem nicht auf portable Weise asynchron ausgeführt werden: Dateisystemzugriffe, DNS-Lookups und bestimmte kryptografische Funktionen. libuv führt diese in einem Thread Pool aus, der standardmäßig vier Threads umfasst und über UV_THREADPOOL_SIZE konfiguriert werden kann.

Aus diesem Grund ist fs.readFile für deinen Code nicht-blockierend, obwohl es im Hintergrund nicht event-gesteuert arbeitet. Das bedeutet auch, dass eine Vielzahl von Dateioperationen in einer Warteschlange hinter dem Pool aus vier Threads landen kann. Die async-APIs halten den Main Thread frei; der Pool übernimmt das Warten.

Den Event Loop blockieren

Alles, was synchron und langsam auf dem Main Thread läuft, blockiert alles: andere Anfragen, Timer und I/O. Häufige Ursachen sind:

  • readFileSync und andere synchrone Datei-Aufrufe in Request-Handlern.
  • Große JSON.parse oder JSON.stringify bei umfangreichen Payloads.
  • Synchrone Kryptografie, wie zum Beispiel pbkdf2Sync mit hohen Iterationszahlen.
  • Endlosschleifen (Tight Loops), Regex-Backtracking und rechenintensive Datentransformationen.
  • Blockierende Bibliotheken, die CPU-intensive Arbeit auf dem Main Thread ausführen.

Messen statt raten: Event Loop Delay und Event Loop Utilization zeigen Ihnen, wie viel Zeit der Loop blockiert ist. Ein steigender Delay unter Last ist das entscheidende Signal.

Arbeit aus dem Event-Loop auslagern

Wenn Aufgaben tatsächlich CPU-gebunden sind, lagern Sie diese aus:

  • worker_threads für Berechnungen innerhalb desselben Prozesses mittels Message Passing.
  • child processes für schwerfälligere oder isolierte Jobs.
  • Ein separater Service für die rechenintensivsten Workloads, der unabhängig skaliert werden kann.
  • Chunking: Zerlegen Sie große Aufgaben in kleinere Stücke und nutzen Sie yield zwischen den einzelnen Schritten.

Nutzen Sie für I/O die async APIs und Streams, damit der Loop frei bleibt.

Best Practices

  • Verwenden Sie async APIs in Request-Handlern; blockieren Sie niemals den Event Loop.
  • Nutzen Sie setImmediate für I/O-Operationen anstelle von rekursiven nextTick.
  • Lagern Sie CPU-intensive Aufgaben in Worker Threads oder Child Processes aus.
  • Streamen Sie große Datenmengen, anstatt diese vollständig im Arbeitsspeicher zu puffern.
  • Optimieren Sie UV_THREADPOOL_SIZE erst nach entsprechenden Messungen.
  • Überwachen Sie Event Loop Delay und die Auslastung in der Production-Umgebung.
  • Halten Sie Callback-Ketten kurz, damit Microtasks den I/O-Durchsatz nicht blockieren.

Häufige Fehler

  • Verwendung von readFileSync innerhalb eines Hot Paths.
  • Rekursive process.nextTick, die den Loop blockieren.
  • Die Annahme, dass Node vollständig single-threaded ist, und das Ignorieren des Thread-Pools.
  • Durchführung rechenintensiver Operationen inline, anstatt diese auszulagern.
  • Die Erwartung, dass setTimeout(fn, 0) vor I/O-Callbacks ausgeführt wird.
  • Ignorieren des Event-Loop-Delays, bis Latenzprobleme auftreten.

Wie geht es weiter?

Der Event Loop ist das Herzstück von Node.js. Machen Sie weiter mit Streams für chunked I/O und dem File System für den asynchronen Dateizugriff. Kehren Sie zu den Node.js Basics zurück, falls Ihnen die Runtime noch neu ist, und messen Sie anschließend den Event Loop Delay in einem Ihrer eigenen Services.

Datei lesen

Asynchrone Arbeit läuft im Thread Pool und hält den Loop frei. Ein synchroner Aufruf blockiert jede andere Anfrage, während die Festplatte antwortet.

Bevorzugt
import { readFile } from "node:fs/promises";

const data = await readFile("big.log", "utf8");
res.end(data);
Vermeiden
import { readFileSync } from "node:fs";

// blocks the entire server
const data = readFileSync("big.log", "utf8");
res.end(data);

Aufgaben planen

nextTick läuft vor Promise-Callbacks und bevor der Loop fortfährt. setImmediate läuft in der Check-Phase nach dem I/O.

Bevorzugt
// after current I/O, don't starve
setImmediate(() => processBatch());
Vermeiden
// recursive nextTick starves
// the loop and blocks I/O
process.nextTick(() => loop());

Häufig gestellte Fragen

Häufig gestellte Fragen

Keep learning

Related topics from the roadmap.

$ Lernen Sie jetzt

Bereit, Event Loop zu lernen?

Unser interaktives Tutorial führt Sie Schritt für Schritt durch Event Loop — mit Quizzen und echtem Code, den Sie im Browser ausführen können.