promise.then chain calling sequence


I want to use Promise to asynchronously implement a recursive call interface for simple AI action sequences. I found that when I first contacted the then, it was not very clear. I referenced some writing methods on the Internet and changed it to my own problems, so first calm down and study the sequence of calls.

example

First look at an example, refer to [1]

 new Promise((resolve, reject) => {
  console.log("promise")
  resolve()
 })
    .then(() => {	// 执行.then的时候生成一个promise是给最后一个.then的
        console.log("then1")
        new Promise((resolve, reject) => {
            console.log("then1promise")
            resolve()
        })
  .then(() => {// 执行这个.then的时候,生成的promise是下面一个then的
   console.log("then1then1")
  })
  .then(() => {
   console.log("then1then2")
  })
    })
    .then(() => {
  // 这个
        console.log("then2")
    })

result:

promise
then1
then1promise
then1then1
then2
then1then2

Question: Mainly doubt that then2 is between then1then1 and then1then2

Demystify

theory

In order to facilitate the analysis, list a few of your own understanding, and it is convenient when explaining the problem later. Especially Theory 4 and Theory 5

Theory 1: Promise is an object

Promise is an object, he contains

  • Own function body: a parameter passed in at new time is a function
  • Status: fulfilled, pending, rejected
  • Asynchronous function queue: then callback body placed here in pending state

Theory 2: resolve / reject is used to change the Promise object

  • resolve: Pending changed to fulfilled
  • reject: pending changed to rejected.
    In addition, when ** resolve is executed, it will check the Promise's own queue. If it is not empty, it will stuff the callback function body into the nextTick queue. ** Code reference [4]
handlers.resolve = function (self, value) {
  var result = tryCatch(getThen, value);
  if (result.status === 'error') {
    return handlers.reject(self, result.value);
  }
  var thenable = result.value;

  if (thenable) {
    safelyResolveThenable(self, thenable);
  } else {
    self.state = FULFILLED;
    self.outcome = value;
    var i = -1;
    var len = self.queue.length;
    while (++i < len) {
      self.queue[i].callFulfilled(value);
    }
  }
  return self;
};

Theory 3: Promise's t'hen / catch method returns a Promise object after execution

Refer to [2], the returned Promise object supports chained calls

Theory 4: When does the Promise object returned by the then function resolve to see the return value

Refer to [2] Return value section

(1)返回了一个值,那么 then 返回的 Promise 将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。
(2)没有返回任何值,那么 then 返回的 Promise 将会成为接受状态,并且该接受状态的回调函数的参数值为 undefined。
(3)抛出一个错误,那么 then 返回的 Promise 将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。
(4)返回一个已经是接受状态的 Promise,那么 then 返回的 Promise 也会成为接受状态,并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。
(5)返回一个已经是拒绝状态的 Promise,那么 then 返回的 Promise 也会成为拒绝状态,并且将那个 Promise 的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。
(6)返回一个未定状态(pending)的 Promise,那么 then 返回 Promise 的状态也是未定的,并且它的终态与那个 Promise 的终态相同;同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。

Theory 5: The execution sequence is: synchronous execution> nextTick queue> setTimeout queue> Promise object private queue

Refer to [3], the article is well written. Generally speaking, I understand the principle of Promise and then. Because the screenshots in the article are not very clear, I still go to github and look at the source code to see the comfort point, refer to [4]

Promise.prototype.then = function (onFulfilled, onRejected) {
  if (typeof onFulfilled !== 'function' && this.state === FULFILLED ||
    typeof onRejected !== 'function' && this.state === REJECTED) {
    return this;
  }
  var promise = new this.constructor(INTERNAL);
  if (this.state !== PENDING) {
    var resolver = this.state === FULFILLED ? onFulfilled : onRejected;
    unwrap(promise, resolver, this.outcome);
  } else {
    this.queue.push(new QueueItem(promise, onFulfilled, onRejected));
  }

  return promise;
};

Brief description:

    1. When the then function is executed, if the Promise state is Fulfilled, the logic of unwrap is executed, and the then function body is placed in a global queue through nextTick
    1. When the then function is executed, if the Promise is in the Pending state, this.queue.push is executed . When you see this, you know that this is placed in a queue of the Promise object itself.

Analysis timing

With the above understanding, let's analyze the printing results that I did not understand at the beginning. Some steps are simple and clear, I may have passed. Of course, because I first came into contact with this, so the steps will be more cumbersome ~

  • 1. When the Promise is executed, the function body executes synchronously and directly resolves, so the Promise object is in the fulfilled state. [Theory 1, Theory 2]
  • 2. When the then1 function is executed synchronously, then itself is a function, but its parameters (function body inside) are not executed. This function body is placed in the nextTick queue [Theory 5]. When then1 is executed, an anonymous Promise object is returned synchronously [Theory 3]. And the state of this Promise object is Pending, because the then1 function body related to this object has not been executed [Theory 4]
  • 3. Synchronously execute then2. As above, it relies on the anonymous Promise object status returned by then1. Because it is pending, then the body of then2 is placed in its own queue of anonymous Promise objects returned by then1
  • 4. The next loop executes the nextTick queue, which is then1's function body
  • 5. First execute a new Promise object function body, print the 'then1 promise'. And the state of this Promise object is resolved to fulfilled
  • 6. Simultaneously execute then.then1.then function, and put her parameters (callback function body) in the nextTick queue. And the returned anonymous Promise object is in the pending state
  • 7. Synchronously execute the then1then2.then function, because the then1then1 callback function body has not yet been executed, returns an anonymous Promise object in the pending state. So the then1then2 function callback function body is placed in the queue of the anonymous Promise object
  • 8. Finally, the then1 function body did not return anything, so according to [Theory 4], the state of this Promise became fulfilled, that is, the state of the first Promise object returned by the then1 function changed. At this time, the state of the resolve function is actually executed. This function will see if there is a callback function in the Promise object's own queue, and then put these into the timeTick queue [theory 3]. Therefore, the callback function body of .then2 is moved from the Promise object's own queue to the nextTick queue. Note that the function bodies of then1, then1 and then2 are in the tick queue at this time, but they are not executed. In addition, js is single-threaded, and the callback function body in the tick queue will be executed in sequence, so then then then1 thenn1 and then2 are executed in order.
  • 9. In the next loop, execute then1, then1 and then2 callback function bodies respectively, so it will print 'then1then1' first, then 'then2'
  • 10. When the then1then1 callback function body is executed, her Promise object will also be switched to fulfilled, and put the then1then2 callback function body in the tick queue
  • 11. The next cycle. Finally execute the then1then2 function body and print 'then1then2'. Perfect ending

Exercise

What if the new Promise in the example is changed to a return?

.then(() => {	// 执行.then的时候生成一个promise是给最后一个.then的
        console.log("then1")
        new Promise((resolve, reject) => {
            console.log("then1promise")
            resolve()
        })

// ===》改成
.then(() => {	// 执行.then的时候生成一个promise是给最后一个.then的
        console.log("then1")
        // 就是这里的加了一个return
        return new Promise((resolve, reject) => {
            console.log("then1promise")
            resolve()
        })

According to [Theory 4], the .then1 function body has a return value, which is a Promise object, and is an anonymous Promise object returned after the execution of then1then2. So only after the Promise object resolves, the logic of then2 callback function body will be executed, so 'then2' will be printed at the end. So the final result is:

promise
then1
then1promise
then1then1
then1then2
then2

reference

[1] Nested Promise Execution Order
[2] Promise.then
[3] Promise Implementation
[4] lie
[5] Promise
[6] Promise Chained Call Order Sequence Thinking

Published 41 original articles · praised 7 · 20,000+ views

Guess you like

Origin blog.csdn.net/pkxpp/article/details/104886823