Browser vs Node.js event loop

Both environments run JavaScript with the same basic rule:

JavaScript runs one piece of code at a time. Asynchronous callbacks wait until the current call stack is free.

The difference is what each environment needs to coordinate.

High-level comparison

BrowserNode.js
Optimized for responsive user interfacesOptimized for server-side input/output (I/O) and throughput
Coordinates JavaScript, user input, Web APIs, and renderingCoordinates JavaScript, timers, network and file I/O
Uses tasks, microtasks, and rendering opportunitiesUses libuv phases plus next-tick and microtask queues
Provides requestAnimationFrame()Provides setImmediate() and process.nextTick()
Must leave time for layout, paint, and user interactionHas no Document Object Model (DOM) or rendering pipeline

Browser implementation

The browser event loop coordinates:

  • JavaScript execution,
  • user events such as clicks and keyboard input,
  • timers and network callbacks,
  • Promise microtasks,
  • style calculation, layout, and paint.

A simplified browser turn is:

Run one task

Drain microtasks

Possibly render

Run another task

The browser’s key concern is responsiveness. Long JavaScript tasks delay input and prevent the next frame from rendering.

requestAnimationFrame() lets code run before the browser’s next paint and is intended for visual updates.

Node.js implementation

Node.js has no rendering pipeline. Its event loop is implemented around libuv, which coordinates operating-system I/O and moves through phases such as:

timers → poll for I/O → check → close callbacks

Node.js also has:

  • process.nextTick(), whose callbacks run before normal Promise microtasks at standard callback boundaries,
  • setImmediate(), whose callbacks run in the check phase,
  • a shared libuv worker pool for selected file-system, Domain Name System (DNS), crypto, and compression work.

Node’s key concern is keeping the main JavaScript thread available to handle many concurrent operations.

Microtasks

Both browsers and Node.js use the V8 microtask queue for:

  • Promise handlers,
  • queueMicrotask(),
  • code that resumes after await.

Both drain microtasks before moving on to ordinary scheduled work.

Node.js adds a separate process.nextTick() queue with even higher priority. Overusing either queue can starve other work.

Workers

  • Browsers use Web Workers for CPU-heavy JavaScript away from the main UI thread.
  • Node.js uses worker threads, each with its own V8 instance and event loop.

Neither worker type directly shares the main JavaScript call stack.

Performance focus

In the browser, ask:

  • Is JavaScript blocking input or rendering?
  • Are frames being dropped?
  • Should visual work use requestAnimationFrame()?
  • Should CPU-heavy work move to a Web Worker?

In Node.js, ask:

  • Is JavaScript blocking other requests?
  • Is the libuv worker pool saturated?
  • Are next-tick or microtask chains starving I/O?
  • Should CPU-heavy work move to worker threads?

Interview answer

Browsers and Node.js share the same run-to-completion JavaScript model, but their event loops are implemented for different host responsibilities. A browser coordinates tasks and microtasks with user input and rendering opportunities, so long work blocks interaction and paint. Node.js has no rendering pipeline; it uses libuv phases to coordinate timers and I/O, and adds process.nextTick() and setImmediate(). The browser is primarily protecting frame and interaction latency, while Node.js is protecting I/O throughput and request latency.