关于promise输出顺序的疑问?

        new Promise((resolve,reject) => {
            console.log('外部promise')
            resolve()
        })
        .then(() => {
            console.log('外部第一个then')
            new Promise((resolve,reject) => {
                console.log('内部promise')
                resolve()
            })
            .then(() => {
                console.log('内部第一个then')
                return Promise.resolve()
            })
            .then(() => {
                console.log('内部第二个then')
            })
        })
        .then(() => {
            console.log('外部第二个then')
        })
        .then(() => {
            console.log('外部第三个then')
        })
        .then(() => {
            console.log('外部第四个then')
        })    

一开始的理解:

  1. 实例化 promise,输出“外部promise”
  2. 执行外部第一个then,回调函数进入队列,外部的第二个、第三个和第四个then依次执行,但由于各自前面的promise都还没有resolve,所以回调函数暂时没有进入队列
  3. 执行外部第一个then的回调函数,输出“外部第一个then”,实例化另一个promise,输出“内部promise”
  4. 执行内部第一个then,回调函数进入队列,由于这个then返回的promise 还没resolve,所以后面跟着的内部第二个then的回调函数暂时没有进入队列
  5. 到这里,外部第一个then的回调执行完了,这个then返回的promise也resolve了,于是外部第二个then的回调进入队列
  6. 执行内部第一个then的回调函数,输出“内部第一个then”,之后返回一个resolve状态的promise,由于这个then的回调执行完了,所以内部第二个then的回调进队列
  7. 执行队列队头第一个回调,输出“外部第二个then”,同时让外部第三个then的回调进队列,接着执行内部第二个then的回调,输出“内部第二个then”
  8. 接着执行外部第三个then的回调,输出“外部第三个then”,同时让外部第四个then的回调进队列
  9. 最后执行外部第四个then的回调,输出“外部第四个then”

所以,输出的顺序应该是:


外部promise

外部第一个then

内部promise

内部第一个then

外部第二个then

内部第二个then

外部第三个then

但代码在chrome运行的时候,输出结果是:

外部promise

外部第一个then

内部promise

内部第一个then

外部第二个then

外部第三个then

外部第四个then

内部第二个then

这里不理解的地方就是,为什么外部所有then的回调都执行之后,最后再来执行内部第二个then的回调?这个和分析的结果不一样。然后我又将代码中的 return Promise.resolve() 去掉,发现打印结果和猜测结果就是一样的了,所以这里的重点应该在于 return Promise.resolve() ,但我不太清楚它具体是做了什么,导致代码输出顺序和预想的不一样。请教各位大佬。

阅读 5.1k
2 个回答
6. 执行内部第一个then的回调函数,输出“内部第一个then”,之后返回一个resolve状态的promise,由于这个then的回调执行完了,所以内部第二个then的回调进队列

then 返回的是一个 Promise (promise.prototype.then)。 这个 Promise 是使用 then 的回调函数的返回值来 resolve 的。

(已经 fulfilled 的 Promise 调用 then ,加入队列并不是直接是回调函数,而是 NewPromiseReactionJob 生成的一个任务。这个任务调用了 then 的回调,并使用这个回调的返回值 resolve then 返回的 Promise)

这里,回调返回的是 Promise().resolve()。这也是一个 Promise 。于是出现了用 Promise 来 resolve Promise 的情况。

用 Promise(0) 来 resolve Promise(1) 有些特殊。Promise(1) 的状态(pending/resolve/reject) 要根据 Promise(0) 来决定,而不是一定会进入 resolved 的状态。即使 Promise(0) 已经是 resolved 了,Promise(1) 也不会立即 resolve 。

(一般的 Promise resolve 里流程见 Promise Reolve Functions。当使用 Promise (严格的说,是 thenable ,即有 then 函数的 object ) 来 resolve Promise 的时候,会通过 NewPromiseResolveThenableJob 生成一个任务,加入队列。生成的这个任务会调用 then)

此时,微任务队列里会加入如下的一个(生成的)微任务(无论 Promise(0) 是什么状态),这个微任务调用 Promise(0) 的 then ,并在这个 then 的回调里 resolve 或者 reject Promise(1)

这里 Promise(0) 就是 (return) Promise.resolve(),而 Promise(1) 是 “内部第一个then返回的 Promise”

7. 执行队列队头第一个回调,输出“外部第二个then”,同时让外部第三个then的回调进队列,接着执行内部第二个then的回调,输出“内部第二个then”

这里并没有执行“内部第二个then”,而是执行上面所述的微任务。

此时 Promise(0) (也就是“内部第一个then”里的 Promise.resolve())的 then 被执行。由于其已经是 resolved 状态,所以其回调被加入队列。

被压入队列的这一个回调执行时会将 Promise(1) (也就是“内部第一个then”返回的 Promise)设置为 resolved 状态。

( NewPromiseResolveThenableJob 生成的任务执行,调用 then ,其回调为 Promise(1) 的 resolve function )

8. 接着执行外部第三个then的回调,输出“外部第三个then”,同时让外部第四个then的回调进队列

在此之后,7 中入队的回调被执行,“内部第一个then”返回的Promise 终于 resolve 了,于是它的 then 的回调(“内部第二个then”)终于被加入队列了。

9. 最后执行外部第四个then的回调,输出“外部第四个then”

在此之后,“内部第二个then” 执行。

return Promise.resolve()会返回一个新的Promise,所以你的内部第二个then已经不是内部的第二个then了,而是一个新的Promise的then。参考Promise.resolve()实现源码:

Promise.resolve = function(parameter) {
  if(parameter instanceof Promise) {
    return parameter;
  }

  return new Promise(function(resolve) {
    resolve(parameter);
  });
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏