When Node.js is started, the event loop is initialized. The event loop consists of 6 stages:
Source code in libuv:
int uv_run(uv_loop_t* loop, uv_run_mode mode) { int timeout; int r; int ran_pending; r = uv__loop_alive(loop); if (!r) uv__update_time(loop); while (r != 0 && loop->stop_flag == 0) { uv__update_time(loop); uv__run_timers(loop); ran_pending = uv__run_pending(loop); uv__run_idle(loop); uv__run_prepare(loop); timeout = 0; if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT) timeout = uv_backend_timeout(loop); uv__io_poll(loop, timeout); uv__run_check(loop); uv__run_closing_handles(loop); if (mode == UV_RUN_ONCE) { /* UV_RUN_ONCE implies forward progress: at least one callback must have * been invoked when it returns. uv__io_poll() can return without doing * I/O (meaning: no callbacks) when its timeout expires - which means we * have pending timers that satisfy the forward progress constraint. * * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from * the check. */ uv__update_time(loop); uv__run_timers(loop); } r = uv__loop_alive(loop); if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT) break; } /* The if statement lets gcc compile it to a conditional store. Avoids * dirtying a cache line. */ if (loop->stop_flag != 0) loop->stop_flag = 0; return r; }
- timer stage: corresponds to the uv_run_timer(loop) method in the source code, which mainly executes the callback that expires in the setTimeout() and setInterval() methods;
- pending callbacks (I/O) stage: corresponds to the uv_run_pending(loop) method in the source code, mainly to execute callbacks that are postponed to the next cycle of I/O operations;
- idle, prepare stage: corresponding to the uv_run_idle(loop) and uv_run_prepare(loop) methods in the source code, which are mainly used internally by Node (don't need to consider it);
- Poll stage: Corresponding to the uv_io_poll(loop, timeout) method in the source code, retrieve new I/O events; execute I/O-related callbacks; in addition, poll is also responsible for detecting whether there is a timer's callback arrival time, and also If not executed, then loop to the timer stage to execute the timer's callback;
- Check stage: Corresponding to the uv_run_check(loop) method in the source code, mainly executes the callback of the setImmediate() method;
- close callbacks stage: Corresponding to the uv_run_closing_handles(loop) method in the source code, it mainly executes some callbacks of close events.
const {readFile} = require('fs'); const {resolve} = require('path'); setImmediate(() => console.log(`[check stage:] setImmediate callback 1`)); setTimeout(() => console.log(`[timer stage:] setTimeout callback 1`), 0); setTimeout(() => console.log(`[timer stage:] setTimeout callback 2`), 0); setImmediate(() => console.log(`[check stage:] setImmediate callback 2`)); setTimeout(() => console.log(`[timer stage:] setTimeout callback 3`), 0); readFile(resolve(__dirname, './exec.js'), 'utf-8', data => { console.log(`[I/O stage] readFile callback 1`); }); Promise.resolve() .then(() => { console.log(`[...to switch to the next stage] Promise callback 1`); console.log(`[...to switch to the next stage] Promise callback 1`); console.log(`[...to switch to the next stage] Promise callback 1`); console.log(`[...to switch to the next stage] Promise callback 1`); console.log(`[...to switch to the next stage] Promise callback 1`); }) .then(() => { console.log(`[...to switch to the next stage] Promise callback 3`); console.log(`[...to switch to the next stage] Promise callback 3`); console.log(`[...to switch to the next stage] Promise callback 3`); }); process.nextTick(() => console.log(`[...to move to the next stage] nextTick callback 1`)); process.nextTick(() => { console.log(`[...to switch to the next stage] nextTick callback 2`); Promise.resolve() .then(() => { console.log(`[...to switch to the next stage] Promise callback 2 in nextTick`); setImmediate(() => console.log(`[check stage:] setImmediate callback 5 in nextTick`)); setTimeout(() => console.log(`[timer stage:] setTimeout callback 5 in nextTick`), 0); }) .then(() => { console.log(`[...to be switched to the next stage] Promise callback 4 in nextTick`); process.nextTick(() => console.log(`[...to be switched to the next stage] nextTick callback 3 in Promise in nextTick `)); }) });
operation result:
[...to transition to the next stage] nextTick callback 1
[...to transition to the next stage] nextTick callback 2
[...to transition to the next stage] Promise callback 1
[...to transition to the next stage] Promise Callback 1
[...to go to the next stage] Promise Callback 1
[...to go to the next stage] Promise Callback 1
[...to go to the next stage] Promise Callback 1
[...to go to the next stage ] Promise callback 2 in nextTick
[...to be switched to the next stage] Promise callback 3
[...to be switched to the next stage] Promise callback 3
[...to be switched to the next stage] Promise callback 3
[... To be switched to the next stage] Promise callback 4 in nextTick
[...to be switched to the next stage] nextTick callback 3 of Promise in nextTick
[timer stage:] setTimeout callback 1
[timer stage:] setTimeout callback 2
[timer stage :] setTimeout callback 3
[timer stage:] setTimeout callback 5 in nextTick
[check stage:] setImmediate callback 1
[check stage:] setImmediate callback 2
[check stage:] setImmediate callback 5 in nextTick
[I/O stage] readFile callback 1
Reference article:
https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/