Event loop - nextTick and microtasks - In the ESM mode and CJS mode of NodeJS, the execution order of nextTick and .then is inconsistent

event loop

Today I want to share the impact of nodejs type: "module" on the event loop.

First, let’s review the basic knowledge of the event loop.

Generally synchronous--> asynchronous, macro-task--> micro-task (macro-tasks are divided into asynchronous and synchronous, < /span> Loop in sequence)Synchronous macro--> Micro--> Asynchronous macro

The details areSynchronization task (script) -----> Clear microtask queue-----> Macro task ...loop

Note thatclear the microtask. If you add the microtask again during the microtask, the microtask will continue to be executed

If a micro task is found during the macro task, even if there are still tasks waiting in the current macro task queue, they will be processed first Complete the micro tasks first before moving on to the next macro task

What are the macro tasks and micro tasks?

Macro tasks:

  1. script (can be understood as outer layer synchronization code)
  2. setTimeout/setInterval
  3. UI rendering/UI incident
  4. postMessage,MessageChannel
  5. I/O operation,setImmediate (Node.js) (What is setImmediate?setImmediate? a>setImmediate has a higher priority than setTimeout ) (because the timer cannot actually be 0ms, it will be later than setImmediate)

Microtasks:

  1. Promise.then
  2. queueMicrotask(() => {}) Create a microtask
  3. Object.observe (Deprecated; replaced by Proxy object)
  4. MutaionObserver

NodeJS-likenextTick column:

  1. process.nextTick (Node.js) (has a separate queue, is executed earlier than the microtask)

Official original words: In each round of the Node.js event loop, the process.nextTick() queue is always processed before the microtask queue

Something goes wrong - In NodeJS, run this code

In fact, today’s focus is not on the above content. With the above knowledge, we try to run the following code:

    console.log('1');
    setTimeout(function () {//time1
        console.log('2');
        new Promise(function (resolve) {
            console.log('4');
            resolve();
        }).then(function () {//then1
            console.log('5')
        })
        process.nextTick(function () {//next1
            console.log('3');
        })
    })
    process.nextTick(function () {//next2
        console.log('6');
    })
    new Promise(function (resolve) {
        console.log('7');
        resolve();
    }).then(function () {//then2
        console.log('8')
    })
    setTimeout(function () {//time2
        console.log('9');
        process.nextTick(function () {//next3
            console.log('10');
        })
        new Promise(function (resolve) {
            console.log('11');
            resolve();
        }).then(function () {//then3
            console.log('12')
        })
    })

According to our conventional analysis, nextTick is executed before the microtask, and the result should be 1 7 6 8< a i=3> 2 4 3 5 9 11 10 12

When the type of package.json is not set, or is set to "commonjs", the output result is indeed in line with our ideas.

image.png

image.png

But when setting type:"module", the output result changes and we get1 7 8 6 2 4 3 5 9 11 10 12

image.png

image.png

Problem analysis and solution

You can try a few more pieces of code where Promise.then and process.nextTick appear at the same time, and you will find that they are inconsistent with expectations.

Can we simply think that "microtasks in module mode have higher priority than then"?

No! Return to the above code and observe again, and you will find that "Promise.then and process.nextTick appear at the same time" multiple times, but in the end a>. In other words, only the outermost microtasks and nextTick have the problem of incorrect order. Only the outermost layer has a sequence change

After reviewing multiple materials with everyone, the final reason is actually: In ESM mode, the code actually runs under async/await

In other words, the processed ESM code actually looks like this

image.png

So, when ESM is running, the code is actually in the microtask phase, and the microtask queue must be cleared before it is nextTick's turn. CommonJS runs synchronously, so you get the expected results

This can also be a good explanation for why only the outermost nexttick has order problems. In fact, the rules are the same as the official documentation:In the Node.js event loop In each round, the process.nextTick() queue is always processed before the microtask queue. The outermost code is in the microtask, and of course there is no nextTick turn. NextTick in setTimeout will be executed before Promise.then.

Now come back to this code:

// CommonJS模式 - 普通分析即可
// 在esm模式中, 由于esm会被包裹在await后执行,所以相当于第一轮代码执行是微任务,这时候的NextTick需要等待微任务清空完成后才能执行。所以先输出 8 再 6。
// 后续在time1中又遇到了nextTick和微任务同时出现的情况,这时候就是普通宏任务环境了,所以 nextTick优先级高于微任务, 先输出 3 再 5
const d = () => {
    console.log('1');
    setTimeout(function () {//time1
        console.log('2');
        new Promise(function (resolve) {
            console.log('4');
            resolve();
        }).then(function () {//then1
            console.log('5')
        })
        process.nextTick(function () {//next1
            console.log('3');
        })
    })
    process.nextTick(function () {//next2
        console.log('6');
    })
    new Promise(function (resolve) {
        console.log('7');
        resolve();
    }).then(function () {//then2
        console.log('8')
    })
    setTimeout(function () {//time2
        console.log('9');
        process.nextTick(function () {//next3
            console.log('10');
        })
        new Promise(function (resolve) {
            console.log('11');
            resolve();
        }).then(function () {//then3
            console.log('12')
        })
    })
}
d()

Summarize

When you encounter the event loop problem during the interview, if you encounter nextTick,explain the difference between CommonJS and ESM clearly, I believe it will be helpful to you Brings extra points

references:

  1. Why the order is inconsistent (see comments):NodeJS process.nextTick and queueMicrotask in commonJs and ESM, what is the execution order? _Big data knowledge base (saoniuhuo.com)
  2. Why ESM is asynchronous: How Node.js handles ES6 modules - Ruan Yifeng's network log (ruanyifeng.com)

Guess you like

Origin blog.csdn.net/m0_64130892/article/details/134536870