How Does the Event Loop Work in Node.js?
Understand the Node.js event loop: its phases, microtasks, and how setTimeout, setImmediate, nextTick, and Promises are ordered. Clear examples included.
Expected Interview Answer
The event loop is the mechanism that lets single-threaded Node.js perform non-blocking I/O by offloading operations to the system and executing their callbacks in ordered phases whenever the call stack is empty.
libuv runs the loop through repeating phases: timers (setTimeout/setInterval), pending callbacks, poll (retrieves new I/O events and runs their callbacks), check (setImmediate), and close callbacks. Between phases — and between each callback — Node drains the microtask queues, running process.nextTick callbacks first and then resolved Promise jobs. This phase-based ordering explains why setImmediate, setTimeout, and Promise callbacks fire in the order they do.
- Enables high concurrency on a single thread
- Avoids the cost of thread-per-connection models
- Predictable callback ordering through defined phases
- Keeps I/O non-blocking and responsive
- Microtasks allow fast, high-priority follow-up work
AI Mentor Explanation
The event loop is like an umpire cycling through fixed duties each over: check the clock, watch deliveries, signal completed runs, handle dismissals, in the same order every time. Urgent appeals (microtasks) are resolved immediately before moving on. This strict rotation is why Node runs callbacks in a predictable sequence rather than randomly.
Step-by-Step Explanation
Step 1
Run synchronous code
All top-level and stack code runs first; the loop starts only when the call stack is empty.
Step 2
Timers phase
Callbacks for expired setTimeout and setInterval timers are executed.
Step 3
Poll phase
New I/O events are retrieved and their callbacks run; the loop may block here waiting for I/O.
Step 4
Check phase
setImmediate callbacks run right after the poll phase completes.
Step 5
Close callbacks
Callbacks like socket 'close' events are handled, then the loop repeats.
Step 6
Drain microtasks
Between every phase and callback, process.nextTick then Promise jobs are fully drained first.
What Interviewer Expects
- Knows the loop enables non-blocking I/O on one thread
- Can name the main phases (timers, poll, check, close)
- Understands microtasks (nextTick, Promises) run between phases
- Explains setImmediate vs setTimeout ordering
- Mentions libuv as the underlying implementation
Common Mistakes
- Thinking the event loop makes JavaScript multi-threaded
- Believing setTimeout(fn, 0) runs immediately or before microtasks
- Ignoring that process.nextTick can starve the loop if overused
- Confusing the call stack, callback queue, and microtask queue
- Assuming setImmediate always runs before setTimeout
Best Answer (HR Friendly)
“The event loop is what lets Node.js do many things at once even though it uses one main thread. It starts slow tasks in the background and comes back to run their follow-up code when they finish, working through a set order of steps so nothing gets blocked.”
Code Example
console.log('1: sync start');
setTimeout(() => console.log('4: setTimeout'), 0);
setImmediate(() => console.log('5: setImmediate'));
Promise.resolve().then(() => console.log('3: promise microtask'));
process.nextTick(() => console.log('2: nextTick'));
console.log('1b: sync end');
// Typical output:
// 1: sync start
// 1b: sync end
// 2: nextTick
// 3: promise microtask
// 4: setTimeout (order vs setImmediate can vary at top level)
// 5: setImmediatePromise.resolve().then(() => console.log('promise'));
process.nextTick(() => console.log('nextTick'));
// Output:
// nextTick
// promiseFollow-up Questions
- What is the difference between setImmediate and setTimeout?
- How do microtasks differ from macrotasks in Node.js?
- Why can process.nextTick starve the event loop?
- What happens in the poll phase specifically?
- How does libuv's thread pool relate to the event loop?
MCQ Practice
1. Which queue is drained between event-loop phases?
Node drains microtasks — process.nextTick callbacks first, then Promise jobs — between each phase and callback.
2. In which phase are setImmediate callbacks executed?
setImmediate callbacks run in the check phase, which comes right after the poll phase.
3. Which library implements the Node.js event loop?
libuv is the C library that implements the event loop and async I/O, including its thread pool.
Flash Cards
What are the main event-loop phases? — Timers, pending callbacks, poll, check, and close callbacks — repeated each iteration.
When do microtasks run? — Between every phase and callback: process.nextTick first, then resolved Promise jobs.
setImmediate vs setTimeout(fn,0)? — setImmediate runs in the check phase; setTimeout in the timers phase. Order can vary at the top level but is deterministic inside I/O callbacks.
What implements the event loop? — libuv, a C library providing the loop, async I/O, and a thread pool.