promise后续(resolvePromise的奇怪点)
前言
上个文章我们利用promsieA+规范实现了promise,这可以帮助我们理解大部分的promise知识并且做解决大部分的面试题以及开发问题,但是却还有很多瑕疵,我会用简单的篇幅让大家了解,让promise在大家眼里变的简单
引用
了解 V8 Promise 源码全过程,世界上不再有能困住你的 Promise 题目,[我就是这么肯定这篇文章的干货](引用月夕大佬的话 (juejin.cn)) 对于月夕关于promise在v8引擎下的描述,从源码到例题,身为菜鸡的我不得不重复看很多遍,也只是感受到了皮毛,但是也总结了一些让大家容易理解,并且可以在面试,或者面试题中有很大信息
图为根据大佬的文章总结的片段导图,接下来为大家阐述
promiseA+关于then的处理
上一篇文章简单描述过A+关于then的回调函数中关于返回值x的处理,我再次带领大家回顾一遍
A+规范判断x是不是一个promise 采用假设给x一个then的属性,如果then是一个函数那么就认定它是一个promise,并且给这个then赋值(2个函数,相当于executor函数中的resolve,或者reject)并且立马确定好函数中的状态 ,如果不是则在没有抛出错误的情况下通过promise的resolve函数包裹出去(确定为fulfilled状态)如以下代码
A+规范的意思是直接同步执行return里面的代码,然后将其的promise对象确定好,然后return出去,按照这种说法,只有下面的then方法的回调函数才会加入到微任务队列中,我们通过一个例子来演示看看其中到底有什么不一样的地方
2.1 return普通值
let p1 = Promise.resolve(1)
let p2 = p1
.then((value) => {
console.log('第一个进入微任务队列')
return '返回一个普通值'
})
.then((value) => {
console.log('接收返回值:', value)
})
let p3 = p1
.then((value) => {
console.log('第二个进入微任务队列')
return '我也return一个普通值'
})
.then((value) => {
console.log('接收返回值:', value)
})
通过执行结果可以看出再return一个普通值的时候好像确实是同步的把1包装成fulfilled状态的promise对象传递下去,我们再通过一个例子来看
2.2 return 包含then方法的对象
let p1 = Promise.resolve(1)
// 测试用例
let p2 = p1
.then((value) => {
console.log('我第一个进入任务队列')
return {
then(resolve) {
resolve('测试值1')
}
}
})
.then((value) => {
console.log(value)
})
// 对比用例
let p3 = p1
.then((value) => {
console.log('我第二个进入任务队列')
return '对比值1'
})
.then((value) => {
console.log(value)
})
如图所示:(resolve1不用注释,主要是可以切换3种状态,调用rekject就是失败,什么都不写就是pending)
(注释部分同上一行效果一样),我们惊奇的发现这和return普通值得情况不一样,分析如下
- 首先测试用例p1.then进入微任务队列,然后对比用例p1.then进入微任务队列 任务队列:[测试p1.then,对比p1.then]
- 然后执行p1.then的代码打印1,然后返回一个对象且有then方法,并且resolve(1),这里如果大家在上一篇文章看过以后就会知道,A+规范在这里通过resovlePromise函数处理这个对象时,已经认定它是一个promise对象,于是将它变为一个fufiled状态的promise对象(因为里面resove了,如果有reject则是rejected的状态,如果啥也没有则是pending状态的promise对象)到这里与浏览器的处理几乎一样,不同的是A+规范认为这是同步的让这个对象转化为promise对象,而V8引擎却是看到这个对象时,开辟了一个微任务队列,将其放入
- 所以在执行完p1的微任务队列后,此时微任务队列为:[对比p2.then,测试返回值为对象的函数]
- 然后执行对比p1的微任务,然后将对比p1末尾的then的回调函数存入微任务队列,此时微任务队列为:[测试返回值为对象的函数,对比p1末尾then的回调函数]
- 后面就是执行测试返回值为对象的函数,然后将测试末尾then的回调函数推入微任务队列,
- 到这里按照正常逻辑就推出这个答案
这是第一个不同的地方
总结:当return 一个包含then方法对象的时候会为其创建一个微任务队列
2.3 return一个promise对象
如下面代码
let p1 = Promise.resolve(1)
// 测试用例
let p2 = p1
.then((value) => {
console.log(1)
return new Promise((resolve, reject) => {
resolve(2)
})
})
.then((value) => {
console.log(value)
})
// 对比用例
let p3 = p1
.then((value) => {
console.log(3)
return 4
})
.then((value) => {
console.log(value)
return 5
})
.then((value) => {
console.log(value)
})
// 1 3 4 5 2
这个例子p1.then返回的是一个promise状态为fulfilled状态的对象,如果我们按照上面那个题的想法,return一个对象或者是promise对象的时候会新创建一个微任务,那么应该打印 1 3 4 2 5 可2却在最后打印,因为当返回一个promise对象加入微任务队列以后,当执行它的时候,会执行它的then方法再创建一个微任务出来,(简单来说就是return一个promsie对象会创建2个微任务,当然肯定不是一次性创建2个微任务哈) 如图
这是月夕大佬在他的文章中描述的,在这里我也思考了很久,虽然用c++实现的源码我也看不太懂
不过感觉掌握这些已经能够应对大部分的面试题了,比如这2道
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);
})
3. 最后
这可能就是语言的魅力吧,遇到奇怪的知识总想把它搞懂,也欢迎大家拿出比较有意思的promise题目和我一起分享。
转载自:https://juejin.cn/post/7152513958736297997