01 / 15

Explain the event loop.

The event loop enables Node.js to perform non-blocking I/O operations. When Node.js starts, it initialises the event loop, to process the input script which may make async API calls, schedule timers, call process.nextTick()

  1. 1

    There are a total of six phases and each phase has a FIFO queue of callbacks to execute.

  2. 2

    When the event loop enters a given phase, it will perform any operations specific to that phase, then execute callbacks in that phase's queue until the queue has been exhausted or the callback limit is reached.

  3. 3

    When the queue has been exhausted or the callback limit is reached, the event loop will move to the next phase, and so on.

Phases Overview
  1. 1

    Timers phase: This phase executes callbacks scheduled by setTimeout() and setInterval().

  2. 2

    Pending callbacks phase: executes I/O callbacks deferred to the next loop iteration.

  3. 3

    Idle, prepare phase: only used internally.

  4. 4

    Poll phase: retrieve new I/O events, execute I/O related callbacks (almost all except close callbacks, the ones scheduled by timers, and setImmediate()), node will block here when appropriate.

  5. 5

    Check phase: setImmediate() callbacks are invoked here.

  6. 6

    Close callbacks phase: some close callbacks, e.g. socket.on('close', ...).

Timer phase
  1. 1

    A timer specifies the threshold after which a provided callback may be executed rather than the exact time we want it to be executed.

  2. 2

    Timer callbacks will run as early as they can be scheduled after the specified amount of time has passed; however, Operating System scheduling or the running of other callbacks may delay them.

  3. 3

    This is the first phase as some timers may have their callbacks pending for execution and thus need to be executed first.

Pending callbacks phase
  1. 1

    The event loop processes some polled events within the poll phase and defers specific events to the pending phase to the next loop iteration.

  2. 2

    It's responsible for executing callback functions that were registered to be called when certain events or asynchronous operations are completed.

  3. 3

    This phase executes callbacks for system operations such as types of TCP errors. For example, if a TCP socket receives ECONNREFUSED when attempting to connect, some *nix systems want to wait to report the error. This will be queued to execute in the pending callbacks phase.

  4. 4

    Asynchronous operations like reading files, making HTTP requests, or handling timers. When these operations are initiated, they typically don't block the main thread but instead register callbacks to be executed when they are completed.

Idle, prepare phase
  1. 1

    The event loop uses the idle, prepare phase for internal housekeeping operations. It doesn’t have a direct effect on the Node.js code you write.

Poll phase
  1. 1

    The poll phase has two main functions: Calculating how long it should block and poll for I/O, then Processing events in the poll queue.

Once the poll queue is empty the event loop will check for timers whose time thresholds have been reached. If one or more timers are ready, the event loop will wrap back to the timers phase to execute those timers' callbacks.

When the event loop enters the poll phase and there are no timers scheduled, one of two things will happen:
  1. 1

    If the poll queue is not empty, the event loop will iterate through its queue of callbacks executing them synchronously until either the queue has been exhausted, or the system-dependent hard limit is reached.

If the poll queue is empty, one of two things will happen:
  1. 1

    If scripts have been scheduled by setImmediate(), the event loop will end the poll phase and continue to the check phase to execute those scheduled scripts.

  2. 2

    If scripts have not been scheduled by setImmediate(), the event loop will wait for callbacks to be added to the queue, and then execute them immediately.

Check phase
  1. 1

    This phase allows us to execute callbacks immediately after completing the poll phase. If the poll phase becomes idle and scripts have been queued with setImmediate(), the event loop may continue to the check phase rather than waiting.

  2. 2

    setImmediate() is a special timer that runs in a separate phase of the event loop. It uses a libuv API that schedules callbacks to execute after the poll phase has been completed.

  3. 3

    As the code is executed, the event loop will eventually hit the poll phase where it will wait for an incoming connection, request, etc. However, if a callback has been scheduled with setImmediate() and the poll phase becomes idle, it will end and continue to the check phase rather than waiting for poll events.

  4. 4

    The event loop executes multiple setImmediate callbacks in the order in which they are created.

Close callbacks phase
  1. 1

    If a socket or handle is closed abruptly (e.g. socket.destroy()), the 'close' event will be emitted in this phase. Otherwise, it will be emitted via process.nextTick().

Difficulty: 5/10
Topics: event loop phases, task queues, microtasks

Scenario Questions

0-2 years experience
  1. 1

    If you call setTimeout with a 0 ms delay inside a request handler, when will its callback run relative to the rest of the code?

  2. 2

    What happens if you perform a CPU‑intensive loop before an async I/O call? How does that affect the event loop and response time?

  3. 3

    How would you use process.nextTick to ensure a piece of code runs before any I/O callbacks?

2-5 years experience
  1. 1

    Your Node service sometimes hangs after handling many concurrent requests. You notice the event loop is blocked. Walk me through how you'd diagnose and fix it.

  2. 2

    Explain why a promise's .then handler runs after a setImmediate callback, and how you might choose between them when streaming data.

  3. 3

    You added a new library that uses setTimeout internally, and now a timeout you set elsewhere fires later than expected. What could the event‑loop ordering be causing this?

5-8 years experience
  1. 1

    Design a high‑throughput API gateway in Node that must avoid event‑loop starvation while handling CPU‑bound tasks. What architectural patterns would you use?

  2. 2

    When scaling a Node microservice cluster behind a load balancer, how does the event loop affect back‑pressure handling and what strategies mitigate latency spikes?

  3. 3

    Explain the trade‑offs of moving a heavy JSON parsing operation to a worker thread versus keeping it on the main event loop.

8+ years experience
  1. 1

    Our legacy monolith runs on Node 8 with a single‑threaded event loop, and we plan to migrate to a microservices architecture. How would you restructure the system to improve scalability while preserving existing async contracts?

  2. 2

    Across multiple teams, some services rely on process.nextTick for ordering, others on setImmediate. How would you establish a consistent event‑loop usage guideline to avoid subtle bugs at scale?

  3. 3

    If we need to guarantee sub‑millisecond latency for a real‑time messaging system, what limitations of the Node event loop must we address, and what alternative runtimes or patterns could we consider?

Follow-up Questions

  • Can you walk me through a concrete example where micro‑task ordering caused a bug?
  • What metrics would you watch to detect event‑loop blockage in production?
  • How does process.nextTick differ from setImmediate in terms of when they run?