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