群友偶然在群里问起的一道关于Promise的题,疑惑于为什么打印结果竟然是两个then一个data,当我以为能像往常一样轻松答出,没想到在我一步步求证的过程中,让我之前很多“想当然”的细节被否决,而网上关于thenable的用法介绍又非常少,我只能对自己对可能出错的细节,列出所有可能进行一个个求证,奋战数日终于攻克,在此记录一番
const thenable = {
then(resolve, reject) {
console.log("then");
resolve("data");
//reject("reason")
},
};
Promise.resolve(thenable).then(
(res) => console.log(res),
(err) => console.log(err)
)
console.log(Promise.resolve(thenable));
一、thenable基本作用
网上查得到的对thenable的介绍:
thenable对象指的是具有then方法的对象。Promise.resolve方法会将这个对象转为Promise对象,然后立即执行thenable对象的then方法。
二、一些细节上的推敲
1.立即执行?
const thenable = {
then(resolve, reject) {
console.log("then");
resolve("data");
//reject("reason")
},
};
Promise.resolve(thenable).then(
(res) => console.log(res),
(err) => console.log(err)
)
console.log(Promise.resolve(thenable));
console.log('aaa'); //新增打印
我们对“立即执行”进行求证,把这串代码稍加修改,打印结果如下
aaa竟然在then和data之前打印?!说明thenable里面的then方法,其实也是异步代码!
而网上资料所说的thenable立即执行里面的then方法,准确的说是在then链式调用的时候,在下一个then方法调用之前,会立即执行thenable里面的then方法,返回由thenable转为的新promise
2.Promise.resolve(thenable).then到底做了什么
一开始,我以为,这是一个then方法,本应该加入到微任务队列中,但是因为thenable返回的新promise是pending状态,所以不执行。
但是,看这段代码
const thenable = {
then(resolve, reject) {
console.log("then");
resolve("data");
//reject("reason")
},
};
Promise.resolve(thenable).then(
(res) => console.log(res),
(err) => console.log(err)
)
打印结果
如果第9行真如上面所猜想一样,因为promise状态是pending所以不执行,那控制台根本就不会有输出!
并且发现,每新增一个Promise.resolve(thenable),就会多打印一个then,说明thenable对象里面的then方法就被调用了一次!
所以我们可以推测得知,在第9行,Promise.resolve(thenable).then()其实做了两件事:
- Promise.resolve(thenable)调用了thenable里面的then方法
- 将这个新产生的Promise所对应的.then()方法,加入到微任务队列中
回顾
最后再来回顾这道题,其实现在看脉络就很清晰了。
其实也可以直接将Promise.resolve(thenable)改造为 new Promise(resolve => resolve(thenable) )
- 碰到一个对象声明,继续往下执行
- 第23行开始,resolve的是一个thenable对象,将thenable对象的执行加入微任务队列, 记作微1。
- 第30行,打印,为主线程任务,所以在控制台会先打印出这个Promise的状态:Pending。32行resolve的同样是一个thenable对象,将thenable对象的执行加入微任务队列, 记作微2。
- 此时主线程清空,开始执行微任务
- 微1执行,打印第一个then,resolve(‘data’)调用,此时将23行的promise状态改位resolved,将第26行对应的then()方法成功回调加入微任务队列,记为微3
- 微2执行,打印第二个then,resolve(‘data’)调用,但此时,31行对应的新promise没有对应的.then()方法,所以微2执行结束
- 微3执行,打印data,此时微任务也全部清空完成,程序运行结束
完结撒花~~~ Promise也不过如此嘛