This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

preface

Hello, everyone, I’m Sanxin Lin. I know a lot about EventLoop in my daily life, but it is also closely related to my daily work. Knowing the execution sequence of EventLoop can greatly help us locate the problem. The normal EventLoop sequence is easy to distinguish, but it is tricky to combine setTimeout + Promise + async/await. Today, I will take you through the five passes to kill six generals and conquer them!!

Note: This article does not cover the Nodejs implementation mechanism

Synchronous && asynchronous

What is asynchronous, what is synchronous, I will not say, I will tell you through a short story.

  • synchronousWhen I call the bookstore to order a book, the boss says I’ll check it out. I don’t hang up the phone and I’m waiting
  • asynchronousI’ll check it out. I’ll tell you later. Hang up the phone and go do your own thing first

JS execution mechanism

In fact, it’s not difficult. JavaScript code execution mechanism, I boil down to three sentences

  • 1, meetSynchronization codeDirect execution
  • 2, meetAsynchronous codePut it aside and put himThe callback functionSave it. The place to save it is calledThe event queue
  • 3, and so onSynchronization codeAll done, then fromThe event queueAll that you have savedAsynchronous callback functionTake it out and do it in order

Look at the following example

console.log(1) / / synchronize
setTimeout(() = > {
  console.log(2) / / asynchronous
}, 2000);
console.log(3) / / synchronize
setTimeout(() = > {
  console.log(4) / / asynchronous
}, 0);
console.log(5) / / synchronizeOutput:1 3 5 4 2
Copy the code

Macro tasks && micro tasks

As mentioned earlier, all asynchronous callbacks are executed sequentially from the event queue after all synchronous code has been executed.

In fact, the event queue is also a small group, and people have their own rules, just like a school runs many clubs, and people have their own rules within the clubs.

Then again, why do event queues need to have their own rules? Why don’t you think about why the school clubs have their own rules to be graded, because some people are strong and some are weak, so there are levels. In fact, event queues are the same, event queues are used for different callback, but asynchronous tasks are also divided into macro tasks and micro tasks, and micro tasks are executed before macro tasks

What are macro tasks and micro tasks?

Macro task

# The browser Node
I/O
setTimeout
setInterval
setImmediate
requestAnimationFrame

Micro tasks

# The browser Node
Promise.prototype.then catch finally
process.nextTick
MutationObserver

Execute the process

Let’s talk about the overall execution process

example

You can follow my steps to solve the problem, basic 90% of the problems are no pressure!!

  • 1. The tag distinguishes asynchronous from synchronous
  • 2. In asynchrony, tags distinguish between macro and micro tasks
  • 3, the number of rounds, one round slowly walk
console.log(1) / / synchronize
setTimeout(() = > {
  console.log(2) // Asynchronous: macro task
});
console.log(3) / / synchronize
Promise.resolve().then(() = >{ // Async: microtasks
  console.log(4)})console.log(5) / / synchronize
Copy the code

The first round

  • Note: The first synchronous execution output
  • Output: 1,3,5
  • Generate macro tasks:setTimeoutAnd generate microtasks:Promise.prototype.then

The second round

  • Note: Microtasks are executed first
  • Output: 4
  • Generate macro task: none. Generate micro task: none
  • Remaining macro tasks:setTimeout, remaining microtasks: none

Round 3 (End)

  • Description: Perform macro tasks
  • Output: 2
  • Generate macro task: none. Generate micro task: none
  • Remaining macro tasks: none. Remaining micro tasks: none

The first level

Think about the way I just said to solve the problem, you can follow that idea, this problem is a minute thing

console.log(1)
setTimeout(() = > {
  console.log(2)
  Promise.resolve().then(() = > {
    console.log(3)})});console.log(4)
new Promise((resolve,reject) = > {
  console.log(5)
}).then(() = > {
  console.log(6)
  setTimeout(() = > {
    console.log(7)})})console.log(8)
Copy the code

Step 1: Mark

Note: Promise executors are synchronized!!

console.log(1) / / synchronize
setTimeout(() = > {
  console.log(2) // Asynchronous: macro task setTimeout1
  Promise.resolve().then(() = > { // Async: microtask then1
    console.log(3)})});console.log(4) / / synchronize
new Promise((resolve,reject) = > {
  console.log(5) / / synchronize
  resolve()
}).then(() = > { // Async: microtask then2
  console.log(6)
  setTimeout(() = > {
    console.log(7) // Asynchronous: macro task setTimeout2})})console.log(8) / / synchronize
Copy the code

Step 2: Take turns

Number of rounds instructions The output produce The remaining
The first round Perform outer synchronization output One, four, five, eight Macro task:setTimeout1

Micro tasks:then2
Macro task:setTimeout1

Micro tasks:then2
The second round Perform microtasksthen2 6 Macro task:setTimeout2

Microtasks: none
Macro task:SetTimeout1, setTimeout2

Microtasks: none
In the third round Executing macro taskssetTimeout1 2 Macro task: none

Micro tasks:then1
Macro task:setTimeout2

Micro tasks:then1
The fourth round of Perform microtasksthen1 3 Macro task: none

Microtasks: none
Macro task:setTimeout2

Microtasks: none
The fifth round Executing macro taskssetTimeout2 7 Macro task: none

Microtasks: none
Macro task: none

Microtasks: none

Second,

If you’re a little confused when you encounter promise.then. Then, you can switch, as we’ll see below

Note: The then method will automatically return a new Promise, that is, return new Promise, the specific source code, you can see this article will be handwritten Promise principle, the most accessible version.

setTimeout(() = > {
  console.log(1)},0)
console.log(2)
const p = new Promise((resolve) = > {
  console.log(3)
  resolve()
}).then(() = > {
  console.log(4)
}).then(() = > {
  console.log(5)})console.log(6)
Copy the code

Step 1: Mark + transform

Note: the conversion here is only for the problem, it is easier to understand, usually do not convert this, usually such conversion is not appropriate, is wrong

setTimeout(() = > { // Asynchronous: macro task setTimeout
  console.log(1)},0)
console.log(2) / / synchronize
const p = new Promise((resolve) = > { // p is the new Promise returned by then1 implementation
  console.log(3) / / synchronize
  resolve()
}).then(() = > { // Async: microtask then1
  console.log(4)
  // Take p then
  p.then(() = > { // Async: microtask then2
    console.log(5)})})console.log(6) 6 / / synchronization
Copy the code

Step 2: Take turns

Number of rounds instructions The output produce The remaining
The first round Perform synchronous output 2,3,6 Macro task:setTimeout

Micro tasks:then1
Macro task:setTimeout

Micro tasks:then1
The second round Perform microtasksthen1 4 Macro task: none

Micro tasks:then2
Macro task:setTimeout

Micro tasks:then2
In the third round Perform microtasksthen2 5 Macro task: none

Microtasks: none
Macro task:setTimeout

Microtasks: none
The fourth round of Executing macro taskssetTimeout 1 Macro task: none

Microtasks: none
Macro task: none

Microtasks: none

The third pass

Once again, if you’re a little confused when you encounter promise.then. Then, you can switch

Note: The then method will automatically return a new Promise, that is, return new Promise, the specific source code, you can see this article will be handwritten Promise principle, the most accessible version.

new Promise((resolve,reject) = >{
  console.log(1)
  resolve()
}).then(() = >{
  console.log(2)
  new Promise((resolve,reject) = >{
      console.log(3)
      resolve()
  }).then(() = >{
      console.log(4)
  }).then(() = >{
      console.log(5)
  })
}).then(() = >{
  console.log(6)})Copy the code

Step 1: Mark + transform

Note: the conversion here is only for the problem, it is easier to understand, usually do not convert this, usually such conversion is not appropriate, is wrong

const p1 = new Promise((resolve, reject) = > { // p1 is the new Promise returned by then1 implementation
  console.log(1) / / synchronize
  resolve()
}).then(() = > { // Async: microtask then1
  console.log(2)
  const p2 = new Promise((resolve, reject) = > { // p2 is the new Promise returned by then2 implementation
    console.log(3) // then1
    resolve()
  }).then(() = > { // Async: microtask then2
    console.log(4)
    
    // take p2 re-then
    p2.then(() = > { // Async: microtask then3
      console.log(5)})})// take p1 to re-then
  p1.then(() = > { // Async: the microtask then4
    console.log(6)})})Copy the code

Step 2: Take turns

Number of rounds instructions The output produce The remaining
The first round Perform outer synchronization output 1 Macro task: none

Micro tasks:then1
Macro task: none

Micro tasks:then1
The second round Perform microtasksthen1 2, 3 Macro task: none

Micro tasks:Then2, then4
Macro task: none

Micro tasks:Then2, then4
In the third round Perform microtasksThen2, then4 4, 6 Macro task: none

Micro tasks:then3
Macro task: none

Micro tasks:then3
The fourth round of Perform microtasksthen3 5 Macro task: none

Microtasks: none
Macro task: none

Microtasks: none

The fourth shut

This level has one more return than the previous level

As we said, the then method will automatically return a new Promise, just like a return new Promise, but if you write a return Promise manually, that’s the Promise you wrote manually

new Promise((resolve, reject) = > {
  console.log(1)
  resolve()
}).then(() = > {
  console.log(2)
  // Return
  return new Promise((resolve, reject) = > {
    console.log(3)
    resolve()
  }).then(() = > {
    console.log(4)
  }).then(() = > { // The implementation of this THEN returns a Promise
    console.log(5)
  })
}).then(() = > {
  console.log(6)})Copy the code

Step 1: Mark + transform

Then4 is then3promise.then (), so it can be converted to then3.then4

new Promise((resolve, reject) = > {
  console.log(1) / / synchronize
  resolve()
}).then(() = > { // Async: microtask then1
  console.log(2) // synchronization in then1
  new Promise((resolve, reject) = > {
    console.log(3) // synchronization in then1
    resolve()
  }).then(() = > { // Async: microtask then2
    console.log(4)
  }).then(() = > { // Async: microtask then3
    console.log(5)
  }).then(() = > { // Async: the microtask then4
    console.log(6)})})Copy the code

Step 2: Take turns

Number of rounds instructions The output produce The remaining
The first round Perform outer synchronization output 1 Macro task: none

Micro tasks:then1
Macro task: none

Micro tasks:then1
The second round Perform microtasksthen1 2, 3 Macro task: none

Micro tasks:Then2, then3, then4
Macro task: none

Micro tasks:Then2, then3, then4
In the third round Perform microtasksThen2, then3, then4 4,5,6 Macro task: none

Microtasks: none
Macro task: none

Microtasks: none

5 off

new Promise((resolve, reject) = > {
  console.log(1)
  resolve()
}).then(() = > {
  console.log(2)
  new Promise((resolve, reject) = > {
    console.log(3)
    resolve()
  }).then(() = > {
    console.log(4)
  }).then(() = > {
    console.log(5)
  })
}).then(() = > {
  console.log(6)})new Promise((resolve, reject) = > {
  console.log(7)
  resolve()
}).then(() = > {
  console.log(8)})Copy the code

Step 1: Mark + transform

const p1 = new Promise((resolve, reject) = > { // p1 is the new Promise returned by then1 implementation
  console.log(1) / / synchronize
  resolve()
}).then(() = > { // Async: microtask then1
  console.log(2)
  const p2 = new Promise((resolve, reject) = > { // p2 is the new Promise returned by then2 implementation
    console.log(3) // then1
    resolve()
  }).then(() = > { // Async: microtask then2
    console.log(4)
    
    // take p2 re-then
    p2.then(() = > { // Async: microtask then3
      console.log(5)})})// take p1 to re-then
  p1.then(() = > { // Async: the microtask then4
    console.log(6)})})new Promise((resolve, reject) = > {
  console.log(7) / / synchronize
  resolve()
}).then(() = > {  // Async: the microtask then5
  console.log(8)})Copy the code

Step 2: Take turns

Number of rounds instructions The output produce The remaining
The first round Perform outer synchronization output 1, 7 Macro task: none

Micro tasks:Then1 and then5
Macro task: none

Micro tasks:Then1 and then5
The second round Perform microtasksThen1 and then5 2,3,8 Macro task: none

Micro tasks:Then2, then4
Macro task: none

Micro tasks:Then2, then4
In the third round Perform microtasksThen2, then4 4, 6 Macro task: none

Micro tasks:then5
Macro task: none

Micro tasks:then5
The fourth round of Perform microtasksthen5 5 Macro task: none

Microtasks: none
Macro task: none

Microtasks: none

The sixth clearance

In fact, the internal implementation principle of async/await relies on continuous nesting of promise.prototype.then, which can also be converted in the topic as described below.

Interested friends can see my article 7 pictures, 20 minutes can be done async/await principle! Why is it taking so long?

async function async1() {
  console.log(1);
  await async2();
  console.log(2);
}
async function async2() {
  console.log(3);
}
console.log(4);
setTimeout(function () {
  console.log(5);
});
async1()
new Promise(function (resolve, reject) {
  console.log(6);
  resolve();
}).then(function () {
  console.log(7);
});
console.log(8);
Copy the code

Step 1: Mark + transform

Note: the conversion here, only for doing the problem, it is easier to understand, usually do not do so conversion, usually such conversion is not appropriate

console.log(4); / / synchronize
setTimeout(function () {
  console.log(5); // Asynchronous: macro task setTimeout
});

// async1 can be converted to
console.log(1) / / synchronize
new Promise((resolve, reject) = > {
  console.log(3) / / synchronize
  resolve()
}).then(() = > { // Async: microtask then1
  console.log(2)})// async1 ends

new Promise(function (resolve, reject) {
  console.log(6); / / synchronize
  resolve();
}).then(function () { // Async: microtask then2
  console.log(7);
});
console.log(8); / / synchronize
Copy the code

Step 2: Take turns

Number of rounds instructions The output produce The remaining
The first round Perform synchronous output Four, one, three, six, eight Macro task:setTimeout

Micro tasks:Then1 and then2
Macro task:setTimeout

Micro tasks:Then1 and then2
The second round Perform microtasksThen1 and then2 2, 7 Macro task: none

Microtasks: none
Macro task:setTimeout

Microtasks: none
In the third round Executing macro taskssetTimeout 5 Macro task: none

Micro tasks:then5
Macro task: none

Microtasks: none

Homework after class

Finally, I give you two homework to help you consolidate the knowledge learned in this article. You can also join me to touch fish and discuss the answer. Click here to join the group, so far nearly 1000 people have joined the study, I will regularly hold learning sharing, mock interview and other learning activities, learn together, make progress together!!

Question 1 (Thinking question)

What’s the difference between these two?

/ / the first
const p = new Promise((resolve, reject) = > {
  resolve()
}).then(() = > console.log(1)).then(() = > console.log(2))

/ / the second
const p = new Promise((resolve, reject) = > {
  resolve()
})
p.then(() = > console.log(1))
p.then(() = > console.log(2))
Copy the code

Question 2 (not a big question)

async function async1() {
  console.log(1);
  await async2();
  console.log(2);
}
async function async2() {
  console.log(3);
}

new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve()
    console.log(4)},1000);
}).then(() = > {
  console.log(5)
  new Promise((resolve, reject) = > {
    setTimeout(() = > {
      async1()
      resolve()
      console.log(6)},1000)
  }).then(() = > {
    console.log(7)
  }).then(() = > {
    console.log(8)
  })
}).then(() = > {
  console.log(9)})new Promise((resolve, reject) = > {
  console.log(10)
  setTimeout(() = > {
    resolve()
    console.log(11)},3000);
}).then(() = > {
  console.log(12)})Copy the code

Question 3 (a little difficult)

This question can be completed within a minute to ask me to receive the prize. Only with certain Promise principle foundation and async/await principle foundation can it be easily answered correctly. If you are interested, you can read the article I have written before

  • Read and write the Promise Principle, the most accessible version ever.
  • 7 pictures, 20 minutes async/await principle! Why is it taking so long?
async function async1() {
  console.log('async1 start')
  await async2()
  console.log('async1 end')}async function async2() {
  console.log('async start')
  return new Promise((resolve, reject) = > {
    resolve()
    console.log('async2 promise')})}console.log('script start')
setTimeout(() = > {
  console.log('setTimeout')},0);

async1()

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

conclusion

If you think this article is a little bit helpful to you, click a like and encourage Lin Sanxin haha. Or you can join my shoal of fish. If you want to enter the learning group and shoal of fish, please click here to shoal of fish. I will broadcast the mock interview regularly and answer your questions