Event Loop
The event loop is what allows Node.js to perform non-blocking I/O operations — despite the fact that JavaScript is single-threaded
This is powered by libuv.
Since most modern kernels are multi-threaded, they can handle multiple operations executing in the background. When one of these operations completes, the kernel tells Node.js so that the appropriate callback may be added to the poll queue to eventually be executed.
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
- Each phase has it’s own FIFO queue of callbacks to execute
- On entering each phase, it will perform any operations specific to that phase, then execute callbacks in that phase’s queue until the queue has been exhausted or the maximum number of callbacks has executed
Note: in the poll phase, events are queued by the kernel and can be queued while polling events are being processed.
Phases
- Timers: execute any
setTimeout()
andsetInterval()
callbacks given that enough time has passed since they were scheduled - Pending callbacks: certain types of I/O callbacks (i.e. TCP
ECONNREFUSED
) - Idle, prepare: Node internals
- Poll: wait for system to call us back for I/O events (normally, this is where Node chooses to block)
- If the poll queue is not empty, the event loop will iterate through its queue of callbacks executing them synchronously until either the queue has been exhausted, or the system-dependent hard limit is reached.
- If the poll queue is empty, one of two more things will happen:
- If scripts have been scheduled by setImmediate(), the event loop will end the poll phase and continue to the check phase to execute those scheduled scripts.
- If scripts have not been scheduled by setImmediate(), the event loop will wait for callbacks to be added to the queue, then execute them immediately.
- Check:
setImmediate()
- Close callbacks:
socket.on('close', ...)
Note: Calling setTimeout(() => {}, 0)
will execute the function at the end of next tick, much later than when using nextTick()
which prioritizes the call and executes it just before the beginning of the next tick.