I recently saw an interesting code for Promise:

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

When I first saw this code, I expected the output to be: 1,2,3,4, but was slapped in the face by the actual output.

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

The code analysis

To understand why the actual output is: 1,2,4,3, let’s walk through the code execution step by step.

We know that when a Promise is instantiated, the incoming callback is executed immediately, and the Promise’s then callback is put into the microtask queue, waiting to be executed. A queue is a first-in, first-out list, and callbacks that are put on the queue first are executed first. In the previous code, there were five callbacks.

This will be fulfilled immediately. At this time, the console prints out the number 1, and the resolve() method is called. The Promise state will be modified to depressing (If the resolve() method is not called, The state of the Promise is pending.

After the Promise is instantiated, the first THEN () method is called, and callback 2 is placed in the microtask queue waiting to execute.

When is the then method called?

If the first then() method is called, the second then() method will be called immediately. If so, the output should be: 1,2,3,4. Obviously, the second then() method is not immediately called, which means callback 5 is not immediately put into the microtask queue. If not, when will it be called?

At this point, take A look at the Promise/A+ specification. The key points are the following:

Promise’s then method accepts two arguments:

promise.then(onFulfilled, onRejected)
Copy the code

2.2.2 If ondepressing is a function:

  • 2.2.2.1 When a Promise is in the processed state, the function must be called with the promise value as the first argument.
  • 2.2.2.2 This function must not be called before the Promise is in the processed state.
  • 2.2.2.3 This function is called not more than once.

2.2.6 THEN can be called multiple times on the same promise.

  • 2.2.6.1 ifpromiseWhen in the processed state, all correspondingonFulfilledCallbacks must follow themthenThe organization order is called in turn.
  • 2.2.6.2 ifpromiseWhen in the rejected state, all correspondingonRejectedCallbacks must follow themthenThe organization order is called in turn.

2.2.7 THEN must return a Promise.

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

// promise1 calls THEN multiple times
// And the execution sequence of ondepressing callback will be fulfilled according to the call sequence of.then
promise1.then(onFulfilled1) / / 1
promise1.then(onFulfilled2) / / 2
promise1.then(onFulfilled3) / / 3
// This is a big pity. The three onFulfilled will be fulfilled in order of 1, 2 and 3
Copy the code
// After calling the.then method, a new promise is returned
promise2 = promise1.then(onFulfilled, onRejected);
Copy the code

To sum up, the first then() method calls and returns a new Promise. The purpose of this is to keep the chain invocation, and the ondepressing callback in the THEN () method will wait until the Promise state changes.

Let’s modify the call form of the previous code slightly 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)})Copy the code

P1.then () returns a new Promise named p2, and the p2.then() callback will be called after the p1.then() callback has been executed, which is when p2, the Promise state, has changed.

Therefore, p2.then() is executed only after callback 2 is complete. Let’s look at callback 2.

This callback will be executed immediately. At this time, the console prints the number 2. Then the resolve() method will be called, and the Promise state will be modified to depressing. The subsequent callback 4 is put into the microtask queue. After callback 2 completes, p2.then() is executed, and callback 5 is placed in the microtask queue.

Callback 4 is executed first and then callback 5 is executed in a queue first-in, first-out order. So, on the console, the number 4 is printed first, and then the number 3.

If you want the output to be 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)})Copy the code

According to rule 2.2.6 above, THEN can be called multiple times on the same promise, and the THEN after P1 will be placed directly into the microtask queue in the order in which they are called.