← Concepts

阅读中文版本

JavaScript Event Loop

The mechanism enabling JavaScript's single-threaded concurrency. It checks whether the call stack is empty and dequeues the next task or microtask.

runtime

Definition

The event loop is a runtime model that orchestrates the execution of code, handling of events, and processing of non-blocking I/O operations. It consists of the call stack, the microtask queue (for promises and MutationObserver), the macrotask queue (/task queue, for setTimeout, DOM events, I/O), and a continuous loop that prioritizes microtasks over macrotasks. When the call stack empties, the event loop first drains all microtasks (FIFO), then processes one macrotask, then drains microtasks again. This cycle repeats.

Context and scope

The event loop is central to understanding asynchronous JavaScript. It explains why setTimeout(fn, 0) does not execute immediately, why Promises resolve before timers, and why long-running synchronous code blocks the UI. It applies to browsers and Node.js with minor differences (Node.js has process.nextTick and setImmediate with their own respective phases).

Common misconceptions

  • setTimeout(fn, 0) executes immediately after the current line — it actually waits for the current macrotask to finish AND all microtasks to drain before the next macrotask begins
  • Promises are macrotasks — Promises create microtasks, which have higher priority and are fully drained between each macrotask
  • setTimeout(callback, 1000) guarantees exactly 1000ms delay — the delay is a minimum; actual execution may be later if the main thread is blocked

Anti-patterns

  • Running long synchronous loops on the main thread blocks all I/O, timers, and user interaction
  • Unbounded microtask generation (e.g., recursive Promise.resolve().then()) can starve the macrotask queue, preventing timers and I/O from executing

Related concepts

Related examples

Authoritative source

https://html.spec.whatwg.org/multipage/webappapis.html#event-loops

What Is the Event Loop

The event loop is the secret sauce behind JavaScript’s ability to handle asynchronous operations without multiple threads. Despite being single-threaded, JavaScript runtimes (V8 in Chrome, Node.js) use the event loop to offload I/O operations to the OS and manage a work queue of callbacks waiting to execute.

How It Works

The event loop follows a simple cycle:

  1. Call stack executes — Synchronous code runs immediately on the call stack.
  2. Async operation dispatched — setTimeout, fetch, DOM events are handed off to the browser/OS.
  3. Callback queued — When the async operation completes, its callback enters the appropriate queue.
  4. Loop cycles — When the call stack is empty, the event loop:
    • First drains all microtasks (Promise callbacks, MutationObserver).
    • Then processes one macrotask (timer, event callback, I/O).
    • Then drains microtasks again.

Example

console.log('1: sync');

setTimeout(() => console.log('2: macrotask'), 0);

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

console.log('4: sync');

// Output order: 1 → 4 → 3 → 2

Behind the Scenes

Modern engines use a tiered queue system:

  • Microtask queue: Promise resolutions, queueMicrotask, MutationObserver callbacks. Drained completely between macrotasks.
  • Macrotask queue (task queue): setTimeout, setInterval, I/O, UI rendering, MessageChannel.
  • Node.js extras: setImmediate (check phase), process.nextTick (microtask-like but even higher priority).

Common Pitfalls

Blocking the loop: A 2-second synchronous loop blocks everything.

// ❌ Bad: blocks the event loop for 2 seconds
function blockingLoop() {
  const end = Date.now() + 2000;
  while (Date.now() < end) { /* spin */ }
}

Fix: Break work into chunks using setTimeout, setImmediate, or requestAnimationFrame.

Infinite microtasks: Generates microtasks that prevent macrotasks from ever running.

// ❌ Bad: starvation — timers never fire
function starveLoop() {
  Promise.resolve().then(() => starveLoop());
}

When to Use the Event Loop Model

  • Debugging why a callback fires later than expected
  • Understanding async/await ordering
  • Optimizing UI responsiveness by keeping the main thread free
  • Designing task schedulers and cooperative multitasking
  • Call Stack: The synchronous execution context
  • Heap: Where objects are allocated in memory
  • Web Workers: True parallelism (separate event loop per worker)