The Event Loop + The Two Kitchens
Dispatched while code runs. Cooked on the kitchen's own clock. Collected only here, at poll.
Between every callback:
drain nextTick, then Promise queue
(repeat until both are empty)
Not tied to any one phase — not animated here.
1
Timers
sticky notes — fire when due
2
Pending Callbacks
rare, housekeeping
3
Idle / Prepare
internal to libuv
4
Poll
the only gate to both kitchens
5
Check
setImmediate callbacks
6
Close Callbacks
cleanup, socket close
out: dispatched during any callback
in: collected only here, at poll
Kitchen One
the order board (OS sockets, epoll)
network calls — HTTP, Postgres,
fetch, Redis
active: 0
No threads — waiting costs nothing.
Kitchen Two
four chefs (libuv thread pool)
fs.*, dns.lookup, crypto,
zlib (gzip, deflate...)
chefs busy: 0/4 · queue: 0
Pool size 4 by default.
Why timing here matters
Both kitchens finish work on their own schedule — the loop doesn't wait for either.
But results only get handed back to your code when the loop's circuit reaches poll.
A busy loop delays every callback — even ones whose work finished long ago.
Normal traffic — everything on its own clock
the loop keeps circling · kitchens handle a few requests
Pause
Restart
Step ▶
0.5×
1×
2×
red dot = the JS thread · teal/amber = requests waiting on a kitchen · poll flashes on delivery · Step ▶ jumps to the next event