Welcome to visit personal blog website: www.zhuxin.club

1. Circulation principle

Node and JS event loops work differently, even though they are executed in the same order after Node 10+. Node is based on the Libev library, js is based on the browser. The core of js event loop is macro task and micro task, while Node has the task execution stage in addition

  1. Timers: The callback functions of setInterval and setTimeout are executed in this phase
  2. Pending Callbacks: Callback functions that perform certain system operations (non-Node)
  3. Idle,prepare: only the system calls it internally
  4. Poll: Retrieves new I/O events, performs I/O-related callbacks ** (including file IO and network IO) **. All event loops and callback processing are executed in this phase. In other cases nodeJS will block when appropriate
  5. check: Executes the setImmediate callback, but not immediately, but when no new event is handled in the poll phase (SetImmediate's callback function will wait until all the callbacks run out)
  6. Close callbacks: Execute some closed callbacks, such as socket.on(‘close’,…) .

Note: setTimeout will default to 1ms if the time is not set or set to 0. At this point, the setTimeout callback function logic will be inserted into the poll queue of the callback function to be executed after the execution of the main process exceeds 1ms.

2. Cycle initiation point

The node.js event loop starts at four points:

  • After node.js is started;
  • SetTimeout callback function;
  • SetInterval callback function;
  • It can also be a callback function after an I/O.

3. Cycle tasks and priorities

Event loops mainly include microtasks and macro tasks:

Micro tasks:

Microtasks in Node have the highest priority (i.e., execution priority) in the event cycle, mainly including Promise and process. nextTick, and Process.nextTick has a higher priority than Promise

Macro task:

There are four types of macro tasks in Node.js — setTimeout, setInterval, setImmediate, and I/O. The macro task is executed after the execution of the microtask. Therefore, in the same event cycle, if both the microtask queue and the macro task queue exist, the microtask queue will be emptied before the execution of the macro task queue. While the main thread handles the callback function, it is also necessary to determine whether to insert microtasks and macro tasks.

Case 1:

const fs = require('fs');

setTimeout(() = > { // The start of a new event loop

    console.log('1'); 

    fs.readFile('./config/test.conf', {encoding: 'utf-8'}, (err, data) = > {

        if (err) throw err;

        console.log('read file sync success');

    });

}, 0);

/// The callback will precede the new event loop

fs.readFile('./config/test.conf', {encoding: 'utf-8'}, (err, data) = > {

    if (err) throw err;

    console.log('read file success');

});

/// This section will be executed in the first event loop

Promise.resolve().then(() = >{

    console.log('poll callback');

});

// The first event loop is executed

console.log('2');



Copy the code

In the code above, there are two macro tasks and one microtask, the macro tasks being setTimeout and fs.readfile, and the microtask being promise.resolve.

The entire process takes precedence over the first event loop of the main thread, so the synchronization logic is executed first, and output 2 first.

Next, the micro-task is performed to output the poll callback.

Then run fs.readFile and setTimeout in the macro task. Since fs.readFile has a high priority, run fs.readFile first. But the processing time is longer than 1ms, so the setTimeout callback is executed first, printing 1. This phase generates a new macro task fs.readfile during execution, so the fs.readfile is inserted into the macro task queue.

Finally, since only the macro task fs.readfile is left, execute the macro task and wait for a callback after the processing completes, printing readFile sync success.

The result is:

2
poll callback
1
read file success
read file sync success
Copy the code

Note: When both setTimeout and I/O red tasks are encountered, only when setTimout is set to 0 will its callback take precedence over the I/O callback. Otherwise, the order of callback execution is not guaranteed.

Case 2:

const fs = require('fs');

setTimeout(() = > { // The start of a new event loop

    console.log('1'); 

    sleep(10000)

    console.log('sleep 10s');

}, 0);

/// will be executed in the poll phase

fs.readFile('./test.conf', {encoding: 'utf-8'}, (err, data) = > {

    if (err) throw err;

    console.log('read file success');

});

console.log('2');

/// function implementation, parameter n unit milliseconds;

function sleep ( n ) { 

    var start = new Date().getTime() ;

    while ( true ) {

        if ( new Date().getTime() - start > n ) {

            // Use break;

            break; }}}Copy the code

The result is:

2
1
sleep 10s
read file success

Copy the code

When executing a sleep10 seconds in the setTimout callback function, fs.readFile is already processed and the main thread is notified to be called. However, the main thread is blocked for 10 seconds while processing the setTimout callback. The fs.readfile callback cannot be processed. Therefore, it can be concluded that the main thread is blocked by the execution of the callback function,

In the example above, if the setTimeOut time was larger, the file IO callback would take precedence over setTimou

4. End of cycle

When all microtasks and macros are cleared, the loop does not end even though there are no current tasks to execute. Because there may be asynchronous I/O that has not yet been called back because other callback functions are blocking, the loop has no end and will execute as long as the process is alive and new tasks exist.

5. Single-threaded or multi-threaded?

The main thread is single-threaded, but Node.js has multi-threaded execution, including setTimeout and asynchronous I/O events. Node.js has other threads, including garbage collection, memory optimization, etc.

The main thread in Node loops through the current event.