preface

In this article, there are several printed interview questions, which are not enough for you, so I have found some corresponding interview questions, and also run on different versions of Node.js. I hope you can also have a good time.

Let’s review the browser Event Loop process:

  • The synchronization code in the current call stack is executed first (a macro task);
  • When the call stack is empty, check whether there are asynchronous tasks (microtasks) that need to be executed;
  • If so, execute the current asynchronous code (all the microtasks in the microtask queue in the current macro task),
  • After that, the next macro task is pulled from the message queue to execute (re-maintain a call stack), and a new round of Event Lopp starts.

An Event Loop differs between different versions of Node.js:

  • Node 10 and earlier: There are several macro tasks in the macro task queue, and the micro tasks in the micro task queue are not executed until all the macro tasks in the macro task queue are completed.
  • If it isNode 11 and later: Once a macro task in the corresponding macro queue is executed in a phase (setTimeout.setIntervalsetImmediateOne of the three, not including I/O, immediately executes the microtask queue, executes all the microtasks in the microqueue and then returns to the macro queue to execute the next macro task. It’sRun the same as the browser.

To do this, I installed the 10.13.0 version of Node.js using NVM (NVM install 10.13.0)

The main competitors are macro task representative setTimeout, micro task bearer Promise and upstart Async/AWIat.

Basic version

The first is the version of a single task:

SetTimeout:

console.log('script start');
setTimeout(() = >  {
  console.log('setTimeout1')},100)
setTimeout(() = >  {
  console.log('setTimeout2')},50)
console.log('script end');
Copy the code

There is a ProcessDelayTask function in Chrome, which calculates expired tasks based on initiation time and delay time, and then executes those expired tasks in turn. The execution sequence is as follows:

Promise:

console.log('script start');
new Promise((resolve) = >  {
  console.log('promise1');
  resolve();
  console.log('promise1 end');
}).then(() = > {
  console.log('promise2');
})
console.log('script end');
Copy the code

Inside promises, they are executed synchronously, so there is the following print order:

Async/awiat:

async function async1() {
  console.log('async1 start')
  await async2();
  console.log('async1 end')}async function async2() {
  console.log('async2')}console.log('script start')
async1();
console.log('script end')
Copy the code

The order of execution is as follows. It is important to note that async2 is also executed synchronously.

The basic version above is the difference between synchronous and asynchronous, because it is a single task and there are no other complex scenarios, and node.js behaves the same as a browser.

Combined Version 1 (setTimeoutPromise)

console.log('script start');
setTimeout(() = >  {
  console.log('setimeout');
}, 0)
new Promise((resolve) = >  {
  console.log('promise1');
  resolve();
  console.log('promise1 end');
}).then(() = > {
  console.log('promise2');
})
console.log('script end');
Copy the code

Order of execution in Chrome:

This version is a combination of simple macro task setTimeout and microtask Promise, because there is only one macro task and one microtask, and it behaves the same in different versions of Node.js as it does in browsers.

Combined Version 2 (setTimeoutPromise,async/await)

console.log('script start')
async function async1() {
  console.log('async1 start')
  await async2()
  console.log('async1 end')}async function async2() {
  console.log('async2')
}
async1()
new Promise(resolve= > {
  console.log('promise1 start')
  resolve()
  console.log('promise1 end')
}).then(() = > {
  console.log('promise2')})console.log('script end')
Copy the code

Order of execution in Chrome:

Order of execution in Node.js 10.13.0:

Execution order in Node.js 12.18.3:

Surprise! No surprise! Chrome (version 91) and Node.js 12.18.3 behave the same, there are some differences in Node.js 10.13.0 (promise2 and Async1 end print order). This means async/await is handled differently in different versions of Node.js (and pre – and post-Chrome 70).

To find out the difference, go to Promise, async, await, execution Order.

Mixed Version 1 (setTimeoutPromise)

console.log('script start')
setTimeout(() = > {
  console.log('setTimeout1')
  Promise.resolve().then(() = > {
    console.log('promise1')})},0)
setTimeout(() = > {
  console.log('setTimeout2')
  Promise.resolve().then(() = > {
    console.log('promise2')})},0)
console.log('script end')
Copy the code

Order of execution in Chrome:

Order of execution in Node.js 10.13.0:

Execution order in Node.js 12.18.3:

This hybrid version mainly looks at the difference between node.js version 10 and node.js version 10.

Mixed Version 2 (setTimeoutPromise)

SetTimeout and Promise:

console.log('script start')
Promise.resolve().then(() = > {
  console.log('promise1')
  setTimeout(() = > {
    console.log('setTimeout1')},0)})setTimeout(() = > {
  console.log('setTimeout2')
  Promise.resolve().then(() = > {
    console.log('promise2')})},0)
console.log('script end')
Copy the code

Order of execution in Chrome:

Order of execution in Node.js 10.13.0:

Execution order in Node.js 12.18.3:

In this version, the first Promise (promise1) is in the microtask queue and the second setTimeout has been added to the end of the message queue. Print the first setTimeout to the end of the message queue (delay queue), so print setTimeout2 and then setTimeout1.

Mixed Version 3 (Promise)

The combination of macro tasks and micro tasks are almost mixed, then look at the mix between micro tasks!

async function async1() {
  console.log('async1 start');
  Promise.resolve(async2()).then(() = > {
    console.log('async1 end'); })}async function async2() {
  console.log('async2');
  Promise.resolve(async3()).then(() = > {
    console.log('async2 end'); })}async function async3() {
  console.log('async3');
  Promise.resolve().then(() = > {
    console.log('async3 end'); })}console.log('script start');
async1();
new Promise((resolve) = >  {
  console.log('promise1');
  resolve();
}).then(() = > {
  console.log('promise2');
});
console.log('script end');
Copy the code

Order of execution in Chrome:

Order of execution in Node.js 10.13.0:

This is also easy to understand, according to the order of the call stack in and out of the stack, the synchronous code is executed first, and then the async3() function is added to the microtask queue, then the async3() function is removed from the stack, back to async2, and the async2 end is added to the microtask queue. Similarly, So you have this print order.

Mixed Version 4 (Promiseasync/await)

async function async1() {
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}
async function async2() {
  console.log('async2');
  await async3()
  console.log('async2 end')}async function async3() {
  await console.log('async3');
  console.log('async3 end')}console.log('script start');
async1();
new Promise((resolve) = > {
  console.log('promise1');
  resolve();
}).then(() = > {
  console.log('promise2');
});
console.log('script end');
Copy the code

Order of execution in Chrome:

Order of execution in Node.js 10.13.0:

The difference between version 4 and version 3 is that the Promise in async functions is await, and the async functions in version 3 are not await, so you can remove the async mark before function.

I think there is no problem until the synchronous code is executed to script end, async is await, so the following code can be understood as the following form:

Promise.resolve().then(() = > {
  console.log('async3 end');
  Promise.resolve().then(() = > {
    console.log('async2 end');
    Promise.resolve().then(() = > {
      console.log('async1 end'); })})})Promise.resolve().then(() = > {
  console.log('promise2');
})
Copy the code

Mixed Version 5 (Promiseasync/await)

function async1() {
  console.log('async1 start');
  Promise.resolve(async2()).then(() = > {
    console.log('async1 end'); })}function async2() {
  console.log('async2');
  Promise.resolve(async3()).then(() = > {
    console.log('async2 end'); })}async function async3() {
  await console.log('async3');
  console.log('async3 end');
}
console.log('script start');
async1();
new Promise(function (resolve) {
  console.log('promise1');
  resolve();
}).then(function () {
  console.log('promise2');
});
console.log('script end');
Copy the code

Order of execution in Chrome:

Order of execution in Node.js 10.13.0:

The async3 end code can be converted to the following form of thinking:

Promise.resolve().then(() = > {
  console.log('async3 end');
  Promise.resolve().then(() = > {
    console.log('async2 end'); })})Promise.resolve().then(() = > {
  console.log('async1 end');
})
Promise.resolve().then(() = > {
  console.log('promise2');
})
Copy the code

conclusion

The paper come zhongjue shallow, and must know this to practice.

Event loops and macro tasks and microtasks

Event Loop Is an Event Loop.

Different callback execution times: Macro and Micro Tasks

Asynchronous Programming Promises: From Use to Handwritten Implementation (4,200 Words)

The Ultimate solution to Asynchronous programming async/await: Writing asynchronous code in a synchronous way