Memory creation vs code execution
Every execution context runs in two phases.
- V8 creates the GEC and pushes it onto the call stack.
- Memory phase:
a,b,callocated asundefined;multiplyFnstored with its full definition (hoisting). - Execution phase: values assigned, code runs top-down.
- Function calls create FECs with the same two-phase pattern.
Three async offload paths
Not every async op uses the thread pool.
- Network I/O (
https.get) → OS kernel non-blocking sockets (epoll / kqueue / IOCP). - File I/O (
fs.readFile) → libuv thread pool (default 4,UV_THREADPOOL_SIZE). - Timers (
setTimeout) → libuv’s internal timer tracking. - DNS & crypto also use the thread pool.
Sync always finishes first
The event loop only runs when V8 is idle.
- V8 runs every sync line, including
multiplyFn()andconsole.log(c), before any callback. - Once the GEC pops, the call stack is empty — the turning point.
- The program does NOT exit: libuv still holds 3 pending async handles.
- The event loop takes over and starts cycling through phases.
Phase cycle with microtasks
What drains between every phase.
- 6 phases: Timers → Pending → Idle/Prepare → Poll → Check → Close.
- Between every phase: drain nextTick queue first, then Promise queue.
- Node 11+: microtasks drain again after every individual callback.
- Poll phase may block waiting for I/O when there’s nothing else to do.
Priority ladder — 7 rungs
Who runs first when everyone is ready.
- (1) Synchronous code
- (2)
process.nextTick()— micro - (3) Promise
.then/.catch— micro - (4)
setTimeout/setInterval— macro - (5) I/O callbacks (fs, http, db) — macro
- (6)
setImmediate()— macro - (7) Close callbacks — macro
Comments
Comments are disabled in this environment. Set
PUBLIC_GISCUS_REPO,PUBLIC_GISCUS_REPO_ID, andPUBLIC_GISCUS_CATEGORY_IDto enable.