7

Recently I saw a very interesting code related to Promise:

new Promise((resolve) => {
  console.log(1)
  resolve()
}).then(() => {
  new Promise((resolve) => {
    console.log(2)
    resolve()
  }).then(() => {
    console.log(4)
  })
}).then(() => {
  console.log(3)
})

When I saw this code for the first time, I thought the output would be: 1,2,3,4 , but I was slapped by the actual output.

As shown in the figure, the actual output is: 1,2,4,3 .

code analysis

In order to find out why the actual output is: 1,2,4,3 , let's analyze the execution of the code step by step.

We know that when the Promise is instantiated, the incoming callback will be executed immediately, and the then callback of the Promise will be placed in the microtask queue for execution. The queue is a first-in, first-out list, and the callbacks that are put into the queue first will be executed first. In the preceding code, there are a total of 5 callback functions.

Callback 1 is the callback when the Promise is instantiated, so it will be executed immediately. At this time, the console prints the number 1 , and then the resolve() method is called. At this time, the Promise state is changed to fulfilled (if the resolve() method is not called, the Promise's Status is pending ).

After the Promise is instantiated, the first then() method is called, and the callback 2 will be put into the microtask queue, waiting to be executed.

When is the then method called?

At this time, the question comes. After the first then() method is called, will the second then method be called immediately? If so, the output should be: 1,2,3,4 . then() method will not be called immediately at this time, that is, the callback 5 will not be put into the microtask queue immediately. If not, when will it be called?

At this time, you need to take a look at the Promise/A+ specification . The focus is on the following:

2.2 then method
The then method of promise accepts two parameters:

promise.then(onFulfilled, onRejected)

2.2.2 If onFulfilled is a function:

  • 2.2.2.1 When the promise is in the resolved state, this function must be called with the value of the promise as the first argument.
  • 2.2.2.2 This function must not be called before the promise is in the resolved state.
  • 2.2.2.3 This function shall not be called more than once.

2.2.6 then Can be called multiple times on the same promise.

  • 2.2.6.1 If promise in the processed state, all corresponding onFulfilled callbacks must be called sequentially in the order in then
  • 2.2.6.2 If promise in the rejected state, all corresponding onRejected callbacks must be called sequentially in the order in then

2.2.7 then must return a promise.

promise1 = new Promise(resolve => resolve())

// promise1 可以多次调用 then
// 且 onFulfilled 回调的执行顺序,按照 .then 的调用顺序执行
promise1.then(onFulfilled1) // 1
promise1.then(onFulfilled2) // 2
promise1.then(onFulfilled3) // 3
// 上面 3 个 onFulfilled,按照 1、2、3 的顺序执行
// 调用 .then 方法后,返回一个新的 promise
promise2 = promise1.then(onFulfilled, onRejected);

To sum up, after the first then() method call, a new Promise will be returned. The aim is to keep the chain calls, and then() in the method onFulfilled callback call will wait until after the Promise state changes.

We slightly modify the calling form of the previous code, as follows:

const p1 = new Promise((resolve) => {
  console.log(1)
  resolve()
})

const p2 = p1.then(() => {
  new Promise((resolve) => {
    console.log(2)
    resolve()
  }).then(() => {
    console.log(4)
  })
})

const p3 = p2.then(() => {
  console.log(3)
})

p1.then() returns a new Promise named p2 , behind p2.then() callback will p1.then() after the callback function in executed, will be called, it is p2 after changing the Promise condition occurs.

Therefore, only callback after 2 executed, will be performed p2.then() . Let's look at the content of callback 2 again.

callback 2 first instantiates a Promise, the instantiated callback is callback 3, the callback will be executed immediately, at this time the console prints the number 2 , and then the resolve() method is called, and the Promise state at this time is modified It becomes fulfilled , and the following callback 4 will be put into the microtask queue. callback 2 is finished, execution p2.then() , callback 5 is placed in the micro-task queue.

According to the first-in, first-out execution order of the queue, execute callback 4 first, and then execute callback 5. Therefore, the console will output the number 4 first, and then output the number 3 .

If you want to output the result: 1,2,3,4 , you can change the code to the following form:

const p1 = new Promise((resolve) => {
  console.log(1)
  resolve()
})

p1.then(() => {
  new Promise((resolve) => {
    console.log(2)
    resolve()
  }).then(() => {
    console.log(4)
  })
})

p1.then(() => {
  console.log(3)
})

According to the previous 2.2.6 rule, then can be called multiple times on the same promise, and the then after p1 will be directly put into the microtask queue according to their calling order.


Shenfq
4k 声望6.8k 粉丝