On this page
- How does the Node.js event loop work?
- Question 1: nextTick, promise and setTimeout
- Question 2: setTimeout vs setImmediate
- Question 3: the same, inside an I/O callback
- Question 4: nextTick inside a promise
- Question 5: async/await
- Question 6: a promise inside a timer
- Question 7: var vs let in a loop
- Question 8: blocking the event loop
- The ES module twist most answers miss
- Quick answers to common Node.js event loop interview questions
- Next steps
These Node.js event loop interview questions show up again and again: "what does this code print?" Below are 8 output puzzles with the answer and a one-line reason you can say out loud. Every output here was produced by running the code on Node.js 26, several times each, so you're learning what Node actually does, not what a blog assumed.
How does the Node.js event loop work?
The event loop is how Node.js runs async callbacks on one thread. Your synchronous code runs first. Then Node keeps cycling through phases, running whatever callbacks are ready. The main phases, in order, are timers, pending callbacks, poll (I/O), check (setImmediate) and close callbacks (Node.js event loop guide).
Between callbacks, Node empties two small queues:
- the nextTick queue (
process.nextTickcallbacks); - the microtask queue (promise
.thencallbacks,awaitcontinuations,queueMicrotask).

That order explains most answers below. All puzzles are CommonJS files (.js) unless they say otherwise.
Question 1: nextTick, promise and setTimeout
console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
process.nextTick(() => console.log('D'));
console.log('E');
Output: A E D C B
Why: sync code (A, E) first; then the nextTick queue (D); then promise microtasks (C); then the timers phase (B).
Question 2: setTimeout vs setImmediate
setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
Output: not guaranteed.
Why: the Node.js docs say this order is "non-deterministic, as it is bound by the performance of the process". In our test it printed immediate first in 20 out of 20 runs, but don't rely on that; the docs show both orders as possible. Saying "it depends, and here's why" is the answer interviewers want.
Question 3: the same, inside an I/O callback
const fs = require('node:fs');
fs.readFile(__filename, () => {
setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
});
Output: immediate timeout (every time)
Why: the callback runs in the poll phase. The check phase (setImmediate) comes right after poll, before the loop gets back to timers. The docs say the immediate is "always executed first" here.
Question 4: nextTick inside a promise
Promise.resolve().then(() => console.log('promise 1'));
process.nextTick(() => console.log('tick 1'));
Promise.resolve().then(() => {
console.log('promise 2');
process.nextTick(() => console.log('tick 2'));
});
process.nextTick(() => console.log('tick 3'));
Output: tick 1, tick 3, promise 1, promise 2, tick 2
Why: both ticks queued during sync code run before any promise. tick 2 is queued while the microtask queue is being drained, so it waits until those promises finish.
Question 5: async/await
async function run() {
console.log('1');
await null;
console.log('2');
}
console.log('3');
run();
console.log('4');
Output: 3 1 4 2
Why: an async function runs synchronously until its first await. Everything after await becomes a microtask, so 2 prints after the remaining sync code (4).
Question 6: a promise inside a timer
setTimeout(() => {
console.log('timeout 1');
Promise.resolve().then(() => console.log('promise inside timeout'));
}, 0);
setTimeout(() => console.log('timeout 2'), 0);
Output: timeout 1, promise inside timeout, timeout 2
Why: in current Node.js, microtasks run after each timer callback, not after the whole batch of timers. So the promise slips in between the two timers.
Question 7: var vs let in a loop
for (var i = 0; i < 3; i++) setTimeout(() => console.log('var', i), 0);
for (let j = 0; j < 3; j++) setTimeout(() => console.log('let', j), 0);
Output: var 3, var 3, var 3, let 0, let 1, let 2
Why: var creates one shared i, which is already 3 when the timers run. let creates a new j for each loop pass. This is really a closure question wearing an event loop costume.
Question 8: blocking the event loop
const start = Date.now();
setTimeout(() => console.log('ran after', Date.now() - start >= 500 ? '500+ ms' : 'less than 500 ms'), 100);
while (Date.now() - start < 500) {}
console.log('loop done');
Output: loop done, ran after 500+ ms
Why: the while loop blocks the only thread. The timer is ready at 100 ms, but it can't run until the sync code finishes. setTimeout(fn, 100) means "at least 100 ms", never "exactly".

The ES module twist most answers miss
Run Question 1 as an ES module (save it as q1.mjs) and the order changes:
CommonJS (.js): A E D C B
ES module (.mjs): A E C D B
Promises now run before process.nextTick. The Node.js docs explain why: ES modules are already being processed as part of the microtask queue, so in ESM queueMicrotask() and promise callbacks run before nextTick ones (Node.js process docs).

Quick answers to common Node.js event loop interview questions
- What is the event loop? The mechanism that lets single-threaded Node.js run async callbacks, by cycling through phases and running ready callbacks.
- Microtask vs macrotask? Microtasks (promises,
queueMicrotask) run right after the current callback. Macrotasks (timers, I/O,setImmediate) wait for their event loop phase. - nextTick vs setImmediate?
nextTickruns before the loop continues;setImmediateruns in the check phase of the next loop pass. The names are confusingly backwards. - Can you starve the event loop? Yes. Recursive
process.nextTickcalls or a long sync loop stop the loop from reaching I/O.
Next steps
Run the puzzles yourself: paste each into a .js file and run node file.js. Then try a real async bug: fix EADDRINUSE address already in use, which happens inside server.listen(). Also useful before interviews: how to decode a JWT token safely.
