Promise - then参数回调函数返回值为Promise对象时产生的两次微任务说明

【JS】因两道Promise执行题让我产生自我怀疑,从零手写Promise加深原理理解 - 听风是风 - 博客园 (cnblogs.com)

Promise.resolve().then(() => {
    console.log(0);
    return Promise.resolve(4);
}).then((res) => {
    console.log(res)
})

Promise.resolve().then(() => {
    console.log(1);
}).then(() => {
    console.log(2);
}).then(() => {
    console.log(3);
}).then(() => {
    console.log(5);
}).then(() =>{
    console.log(6);
})

一开始做这题我给出的答案是:0 1 4 2 3 5 6

给出这个答案是因为我对于then参数回调函数返回值与then方法返回值的关系的理解不够透彻。我虽然知道:

  1. 当then参数回调函数返回值res为非Promise类型对象时,则then方法返回的Promise对象的状态为fulfilled,结果值为res
  2. 当then参数回调函数返回值res为Promise类型对象时,则then方法返回的Promise对象thenRes的状态为res的状态,结果值为res的结果值

对于2,我们要需要更加深入了解如下问题:

res与thenRes是否为同一个对象?

通过以上例子验证,res和thenRes并非同一个对象。

thenRes如何实现与res状态相同,结果值相同?(第一次微任务关键点)

thenRes = new Promise((resolve, reject) => {
    res.then(data => resolve(data), reason => reject(reason))
})

通过以上方式就可以创建一个状态和结果值与res相同的新的Promise对象thenRes。

我们知道即使调用then的promise对象是非pending的,promise.then也不会直接调用then的参数回调函数,而是将参数回调函数加入异步微任务队列,等待同步代码执行完后,异步地执行。这意味着4的打印会延迟一次,即最终打印结果为0 1 2 4 3 5 6 

但是实际上该段代码的执行结果为

这意味着实际上4的打印还要再延迟一次,即还有一次微任务产生,那么到底是哪里产生的呢?实际上只有两种可能:

  1. Promise.resolve(4)产生的,可能Promise.resolve静态方法内部会产生一次微任务
  2. return Promise.resolve(4)产生的,即还是then内部产生的,可能除了Promise对象复制产生的一次微任务外,还有一次不知道啥地方产生的微任务

验证方式也很简单,只需要将代码中return Promise.resolve(4) 变动一下即可:

 

Promise.resolve静态方法作用是快速创建一个fulfilled状态的promise对象, 底层实现还是使用的new Promise方式,为了避免Promise.resolve内部存在隐藏逻辑,我们直接new Promise,发现其实没有影响,最终输出结果不变。

 当我们直接return 4时,则会减少一次由于Promise对象复制产生的then开启的微任务,理论上应该打印出来是 0 1 2 4 3 5 6,但是时机结果却是 0 1 4 2 3 5 6,即直接减少了两次微任务。

说明 return "Promise对象",会开启两次微任务,一次是Promise对象复制的then调用,一次是隐式的开启的。

至于return "Promise对象"的隐式开启的微任务作用是啥,为啥要多开启一次几乎无意义的微任务,大家可以参考下(6 封私信) promise.then 中 return Promise.resolve 后,发生了什么? - 知乎 (zhihu.com)

中各个大神的解释,反正

最终结论:then方法参数回调函数返回Promise对象是会开启两次微任务:

  • Promise对象状态和结果值复制触发的then
  • 隐式开启的微任务

最终现象是:若then方法的参数回调函数返回值为Promise对象,则会影响then方法返回值Promise对象的pending状态延迟2个微任务后改变。

另外不仅仅是then参数回调函数的返回值为Promise对象会开启两次微任务,对于excutor执行器的形参resolve,reject函数,如果它们也接受道一个Promise对象为入参,则同样会开启两次微任务,见如下笔试题:

道理是一样的。

另外需要注意的是对于 Promise.resolve静态方法而言:

  • 如果入参是一个非Promise对象的值res,则返回一个fulfilled状态,结果值为res的Promise对象
  • 如果入参是一个Promise对象res,则直接返回该res

验证如下

所以总结如下

  • Promise.prototype.then的参数回调函数,以及Promise#resolve方法,当他们入参为一个Promise对象时,内部会开启两次微任务
  • Promise.resolve,当入参为Promise对象时,会直接返回该入参Promise对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员阿甘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值