V8 + libuv split duties
One brain, one nervous system.
- V8 (C++) runs JavaScript on the single main thread — call stack, heap, GC.
- libuv (C) provides async I/O: event loop, thread pool (default 4), callback queues.
- V8 delegates
fs,https.get,crypto, DNS to libuv; libuv talks to the OS. - The main thread is sacred — nothing interrupts it; callbacks wait in queues.
Six phases, clockwise cycle
Timer → Pending → Idle → Poll → Check → Close.
- Timer:
setTimeout,setInterval— delay is a minimum, not exact. - Pending callbacks: deferred I/O callbacks (TCP errors etc).
- Idle / Prepare: internal Node housekeeping.
- Poll: I/O results from
fs,http,net, crypto. Loop parks here if idle. - Check:
setImmediate()— runs right after Poll. - Close:
socket.on('close')cleanup.
Microtasks drain between every phase
Two VIP queues ahead of every macrotask.
- Between every phase: drain ALL
process.nextTick()→ then ALL Promise callbacks. - A nextTick scheduled during draining still runs in the same drain.
- Node 11+: microtasks also drain after every individual callback.
- Recursive
process.nextTick()can starve the event loop — usesetImmediate()instead.
setTimeout(0) vs setImmediate
Order depends on context.
- Outside I/O: order is non-deterministic — depends on process boot timing.
- Inside I/O (Poll callback):
setImmediatealways beatssetTimeout(0)— Check comes right after Poll. setTimeout(fn, 0)still waits for Timers phase on the next loop iteration.- Poll → Check → Close → (back to Timers) is the fixed order.
Execution priority — 7 rungs
When everything is ready, who runs first.
- (1) Synchronous code — always first.
- (2)
process.nextTick()— highest-priority microtask. - (3) Promise
.then/.catch/.finally— second microtask tier. - (4)
setTimeout / setInterval— Timers phase. - (5) I/O callbacks — Poll phase (
fs,http, crypto). - (6)
setImmediate()— Check phase. - (7) Close callbacks — final cleanup.
Seven golden rules to memorise
The cheat sheet for interviews.
- Sync always runs first. Always. No exceptions.
process.nextTick()> Promises > every phase.- Microtasks drain between every phase.
- Inside I/O,
setImmediatebeatssetTimeout(0). - Outside I/O, that order is non-deterministic.
- When idle, the loop parks in Poll, waiting for I/O.
- Recursive
nextTick()starves the loop — reach forsetImmediate()for recursive async.
Comments
Comments are disabled in this environment. Set
PUBLIC_GISCUS_REPO,PUBLIC_GISCUS_REPO_ID, andPUBLIC_GISCUS_CATEGORY_IDto enable.