Notes:
- Text in [[ ]] are the internal libuv function call.
- Text in {{ }} are the Node functions that are affected.
- Text in ( ) are notes about what is happening.
- While the Windows event loop has minor variations, I don't believe any of those affect Node.
On process.nextTick()
:
process.nextTick()
is poorly named. It doesn't actually wait until the "next
tick" of the event loop. Instead it runs any callbacks passed directly after
the synchronous execution of the current phase of the event loop (i.e. before
the event loop can continue to the next phase).
This translates to the fact that the nextTickQueue
(i.e. the mechanism that
contains all callbacks passed to process.nextTick()
during the execution
of any callback) is processed at the end of every phase of the event loop.
This is poorly used in Node in cases like "close" events, where the event is
emitted asynchronously but only by means of process.nextTick()
. Instead of
actually calling the close callback in the final phase of the event loop
(uv__run_closing_handles()
).
There has been discussion on correcting this, but my initial attempt broke several tests and I haven't taken the time to figure out why.
Brief overview of the current state of the event loop, and what runs at any given time.
┌───────────────────────┐
│ Application Startup │
└───────────┬───────────┘
│
│ (bootstrap global environment)
│
┌───────────┴───────────┐
│ [[ uv_run() ]] │
└───────────┬───────────┘
│
┌─────────────┴─────────────┐
╭────┤ [[ uv__run_timers() ]] │
│ ┢━━━━━━━━━━━━━━━━━━━━━━━━━━━┪
│ ┃ {{ setTimeout() }} ┃
│ ┃ {{ setInterval() }} ┃
│ ┗━━━━━━━━━━━━━┯━━━━━━━━━━━━━┛
│ │
│ ┌─────────────┴─────────────┐
│ │ [[ uv__run_pending() ]] │
│ ┢━━━━━━━━━━━━━━━━━━━━━━━━━━━┪
│ ┃ All completed write reqs. ┃
│ ┃ ┃
│ ┃ Error Reporting for: ┃
│ ┃ - TCP ECONNREFUSED ┃
│ ┃ - PIPE (all errors) ┃
│ ┗━━━━━━━━━━━━━┯━━━━━━━━━━━━━┛
│ │
│ ┌─────────────┴─────────────┐
│ │ [[ uv__run_idle() ]] │
│ ┢━━━━━━━━━━━━━━━━━━━━━━━━━━━┪
│ ┃ {{ clearImmediate() }} ┃
│ ┗━━━━━━━━━━━━━┯━━━━━━━━━━━━━┛
│ │
│ ┌─────────────┴─────────────┐
│ │ [[ uv__run_prepare() ]] │
│ ┢━━━━━━━━━━━━━━━━━━━━━━━━━━━┪
│ ┃ CPU Idle Profiler ┃
│ ┃ (Inform V8 profiler that ┃
│ ┃ Node is about to idle) ┃
│ ┗━━━━━━━━━━━━━┯━━━━━━━━━━━━━┛
│ │
│ ┌─────────────┴─────────────┐
│ │ [[ uv__io_poll() ]] │
│ ┢━━━━━━━━━━━━━━━━━━━━━━━━━━━┪
│ ┃(Poll for incoming or ┃
│ ┃completed events signaled ┃
│ ┃back from the kernel) ┃
│ ┃ ┃
│ ┗━━━━━━━━━━━━━┯━━━━━━━━━━━━━┛
│ │
│ ┌─────────────┴─────────────┐
│ │ [[ uv__run_check() ]] │
│ ┢━━━━━━━━━━━━━━━━━━━━━━━━━━━┪
│ ┃ {{ setImmediate() }} ┃
│ ┃ ┃
│ ┃ CPU Idle Profiler ┃
│ ┃ (Inform V8 profiler that ┃
│ ┃ Node is no longer idle) ┃
│ ┗━━━━━━━━━━━━━┯━━━━━━━━━━━━━┛
│ │
│ ┌───────────────┴───────────────┐
│ │[[ uv__run_closing_handles() ]]│
│ ┢━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┪
│ ┃(Currently not used by Node as ┃
╰──┨all close callbacks are instead┃
┃called prior to this via. ┃
┃process.nextTick()) ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
Is this picture still up to date? What does
clearImmediate()
do inuv__run_idle()
? Couldn't find the call in the sources.