Node.js event loop mechanism

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.
Code sample:
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/

https://github.com/libuv/libuv

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325599032&siteId=291194637