Deviopath › Node.js

Simulate the Node.js Event Loop

Hard · Event Loop

Node.js runs callbacks in a precise order: synchronous code, then microtasks (process.nextTick and promises), then event loop phases (timers, check for setImmediate...). Write eventLoopOrder(program) that simulates that order and returns the printed lines.

A program is an array of operations. Operations with a callback have a list of child operations, which run when the callback runs:

- ["log", text] prints text.

- ["nextTick", children] is process.nextTick(cb).

- ["promise", children] is Promise.resolve().then(cb).

- ["timeout", ms, children] is setTimeout(cb, ms).

- ["immediate", children] is setImmediate(cb).

Rules (Node.js 11+, CommonJS). The whole program runs as the body of an I/O callback (as if inside fs.readFile(file, () => { ... })), which makes the order deterministic:

- Synchronous code runs first, top to bottom.

- After every callback (the program itself, each timer, each immediate), drain the microtasks: run the whole nextTick queue (including ticks added meanwhile), then the whole promise queue (including promise callbacks added meanwhile), and repeat until both are empty. So a nextTick scheduled inside a promise callback runs only after the promise queue is empty.

- Because we start in an I/O callback, the check phase comes next: it runs the immediates queued before the phase started. Immediates added during the phase wait for the next loop iteration.

- Then the timers phase runs every due timer, ordered by due time, then by scheduling order. A timer is due at (time it was scheduled) + ms, where an ms of 0 counts as 1 (Node turns delays below 1 into 1). Timers created during this phase are never due in the same phase.

- Then check again, timers again, and so on until nothing is left.

- Time: callbacks take no time. The clock (starting at 0) only moves when nothing is runnable: before a timers phase, if no immediate is queued and no timer is due, jump to the earliest due time.

Note: when a program runs directly in the main module (not in an I/O callback), setTimeout(cb, 0) versus setImmediate(cb) order is not deterministic in real Node. Also, whether a timer fires between two chained immediates depends on real elapsed time. The tests only use programs whose output real Node always prints in the same order.

Next: Schema Validator