then是什么时候进入微任务队列的?

想请教下我对于以下这段代码的理解是否正确:

new Promise((resolve, reject) => {
 console.log(1);
  resolve();
})
.then((a) => {
 console.log(2);
  new Promise((resolve,reject) => {
   console.log(3);
    resolve();
  })
  .then((c) => {
   console.log(4);
  })
  .then((d) => {
   console.log(6);
  })
})
.then((b) => {
 console.log(5);
});

输出是1 2 3 4 5 6
我疑惑的点主要在于:then的回调究竟是什么时候放入微任务队列中的,特别是console.log(5)console.log(6)这两个then

在参考了A+规范后,有这么一段话:promise.then(callback)的回调必须在promise变为fulfilled之后调用

首先执行代码块,Promise构造函数直接输出1,而后这个Promise通过调用resolve变为fulfilled,因此将then(a=>)放入微任务队列。接下来执行then(a=>)的回调:
首先打印2,然后new Promise打印3,调用resolve状态变为fulfilled
因为这个Promise现在是fulfilled了,所以将then(c=>)放入微任务队列
所以现在的微任务队列:then((c)=>)。按照我的理解,到目前为止,then((b)=>)是不在微任务队列中的,所以关键就在于,then(b)和then(c)是什么时候进入微任务队列的呢?

阅读 2.3k
2 个回答

注意 then(a=>) 的回调执行结束之后(此时 c=> 已经在微任务队列里了),then(a=>)的返回的 Promise 就 fullfill 了(注意这个 Promise 不是最开始的 new Promise)。于是,这个返回的Promise 的 then(b=>) 被加入了微任务队列。


promise1.then() 会返回一个新的 promise ,这个新的 promise 不是 promise1 。这个新的 promise 会根据 then 回调的执行结果来设置自己的状态,也就是只有回调正常结束了才会 fullfill 。

链式调用 then 时,每个 then 都是在各自不同的 promise 对象上调用的,而不是他们共享一个 promise 。


回答下评论:

所以then(b=>是在将then(c=>加入微任务队列之后再加入的吗?我可不可以理解为,在执行then(a=>)的回调过程中,js引擎看到了then(c=>)这个then的调用链,所以会直接将then(c=>)加入微任务队列,而then(d=>)其实是被刻意忽略了。那么这时then(a=>)的执行就结束了,于是才将then(b=>)加入微任务队列?

问题在于,调用 then 就会将回调加入微任务嘛?不是的。只有当调用的 promise 已经 fullfilled 的时候,调用 then 才会将回调加入微任务。如果调用的 promise 的状态是 pending ,那么调用 then 不会加微任务,而只是将回调保存在 promise 的“属性([[PromiseFulfillReactions]])”里面。当 promise 的状态由 pending 变化为 fullfilled 的时候,这个属性的里的所有记录会被加进微任务队列。

于是调用 then(a=>), 回调会被加入微任务,因为调用时 promise 已经 fullfilled 了。同时,他返回了一个新的 promise (记作 promise1 吧) ,这个新的 promise1 状态时 pending 。在新的 promised1 上调用 then(b=>)(这个调用是同步的) ,这个回调不会被加入微任务,因为此时这个新的 promise1 的状态是 pending ;这个回调会被记录在 promise1[[PromiseFulfillReactions]] 里。当 a=> 回调从微任务队列被取出并执行完之后,promise1 的状态由 pending 变化为 fullfilled ,它在 [[PromiseFulfillReactions]] 中记录的回调会被放入微任务队列(b=> 入队)。

c, d 同理。

具体的执行过程应该是这样的:
1、new Promise,打印1,紧接着调用resolve(), 之后state会变成fullfiled,变成fullfiled后,then中的回调函数,会进入到微任务队列中, 当前任务下已经没有同步任务时, 就开始执行微任务队列中的任务
2、 接着第一步, 开始执行回调函数,按照同步任务的执行顺序, 分别打印123,同理, 打印完3之后,调用resolve,state改变,then中的回调函数会进入到微任务队列中(此时微任务队列中只有 (c)=>{console.log(4)})。当前函数执行完之后。state的状态也会被改变,之后会将(b)=>{console.log(5)}放入微任务队列中。注意,此时微任务队列中会有两个任务, 分别是任务c和任务b
3、由于没有其他同步任务执行,所以js引擎会继续执行微任务队列中的任务, 拿到队首的任务,即任务c,开始执行,打印出4, 此时还是状态改变,将函数(d)=>{console.log(6)}入队
4、继续循环微任务队列,此时, 微任务队列中存在两个任务, 任务b和任务d,取b任务,执行,打印出5,之后 执行d,打印出6

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题