问题
前段时间在使用Promise的过程中遇到一个很疑惑的地方。大概是这样的:
const p = new Promise((resolve, reject) => {
return 'hello world'
}).then((result) => {
// expected --> 'hello world'
console.log(result)
})
结果并不能console出hello world,也就是then里面的callback并没有执行到.
如果并非在Promise实例内返回值,而是resolve,则可以console出hello world
const p = new Promise((resolve, reject) => {
resolve('hello world')
}).then((result) => {
// expected --> 'hello world'
console.log(result)
})
一直想当然以为new Promise(callback)和then(callback)中callback的使用是一样的。略疑惑,于是去看了下then/promise的大致实现,发现和自己的思路完全不一样,于是做下记录。
解答
由于是被"bug"吸引过来的,所以第一时间看了Promise的构造函数
Promise.png
doResolve.png
可以发现,由于我们的fn并没有调用resolve,所以res的值并没有被记录,只有调用了reslove,value才能够记录在Promise实例的_value中。
resolve.png
源码理解
乍一眼看源码,有种晕晕的感觉。core里函数也不多,随手分了下类:
分类.png
考虑如下:
-
Promise构造函数: 定义基本状态。暴露出来的then方法: 创建新的Promise; - 考虑日常调用: 由于
Promise实例需要调用resolve/reject才能继续,所以resolve/reject符合回调模式,resolve/reject的后续调用应该是拿来改变各种Promise内部状态(突破口); -
Handler就三行,看上去是包装了一下onFulfilled和onRejected,保留then所新建的Promise指针; - 剩余的
handle/handleResolve/doResolve/finale不是很好理解,留待慢慢分析;
Promise构造函数
结合作者注释,可整理如下:
promise实例.png
内部属性解释:
- ** _state** (Int): 记录的是自身的情况;
- _deferredState (Int): 记录的是当前promise实例的then的情况;
-
_deferreds (Handler Array): 记录的是当前
Promise实例之后的then(也就是Handler实例,不理解稍后讲解); - _value: 记录的是当前promise异步完得到的值,可能是实值也有可能是另一个promise.
_state保存异步情况, 其可能值如下:
0 : 获取中;
1 : 获取成功;
2 : 获取被拒绝(失败);
3: 需要获取另外一个异步结果.
_deferredState看上去有点奇怪,其可能值如下:
0: 初始值;
1: 当前Promise实例后续只有一个then的时候;
2: 当前Promise实例后续有多个then的时候.
你一定要大叫: "什么鬼!!"
考虑这样的情况
const p = new Promise(resolve => resolve('a'))
p.then((result) => {
console.log(result) // 'a'
})
p.then((result) => {
console.log(result) // 'a'
})
为何要这么记录_deferredState呢?
因为handleResolved函数每次只处理一个deferred嘛.
then
唯一暴露出来的then方法, 做的事情是创建一个新的Promise实例,并和当前Promise实例进行_state和_deferredState千丝万缕的关联。如果我们创建一个Promise实例,并多次调用then方法,过程基本上是酱紫的:
then! then! then!.png
resolve / reject
resolve方法接受(self(当前Promise实例), newValue(异步执行结果)),干的事情基本符合猜想:
- 处理出错(
newValue == self || getThen(newValue)失败)的情况: 抛锅给reject; - 如果发现
newValue是Promise实例,当然是标注_state为adopt another promise,然后把_value指向新的Promise实例(newValue),再进入收尾函数finale; - 如果发现
newValue是function,就跟处理new Promise(fn)一样进入doResolve; - 剩余
newValue的情况无非就是Number、String等值了,此时当前异步已完成,修改状态_state为fulfill,并记录_value值为newValue,再进入收尾函数finale.
reject就更简单了:
- 更改状态
_state为reject,然后进入收尾函数finale.
收尾函数finale看上去好厉害哦,不晓得干了些什么事情.
收尾函数finale.png
根据resolve和reject的处理逻辑,只有在
-
newValue为Promise实例; -
Number等正常值时;
(进入doResolve线的最后还是要走这两条路子) - 执行函数失败时;
才会进入finale. finale所做的事情是针对_deferredState的取值进入不同的处理。
根据之前的认知,_deferredState记录的是当前Promise后续有一个then还是多个then。结合代码来看其实很容易理解啦,就是将多个then逐一经过handle处理.
handle
一旦读懂_deferredState的作用,handle简直不在话下嘛。
调用handle函数只有两个地方(safeThen和then记为一处,另外是finale)。这两块地方代码几乎不重用。不是很理解为何在同一处进行处理。
handle.png
首先理解while,根据作者注释,_state为3的意义即是: adopt another Promise,也就是这样的情况:
new Promise(resolve => resolve('a'))
.then(() => {
return new Promise(resolve => resolve('b')) // 标记
})
.then((result) => {
console.log(result) // 'b'
})
之前谈到resolve的时候谈到过如果newValue也是Promise实例或者是正常值,都会被记录到_value中,此处代码的意义也就是拿到标记处异步的最终结果啦~
- 从
then或是safeThen进入: 此时self._state的值应该还是0,通过判断当前Promise实例的后续个数,_deferreds收集到后续所有的deferred(其实就是Handler实例啦),// 讲人话就是跟在当前Promise屁股后面有多少个then啦 - 从
finale进入: 此时self._state的值实际上为1或者2,反正是处于解决的状态,为何不是3?因为前面while了嘛。此时不会经过self._state === 0的判断,而是直接走向handleResolved了 // 终于干正事了
handleResolve
handleResolve简直是core代码里面的高潮嘛~
这里做的处理是从当前Promise实例过渡到下一个deferred(也就是Handler,也就是当前Promise屁股后面的then啦)
handleResolve.png
稍微解释下asap,看上去应该是类似将当前fn转成microtask,在当前event loop末尾执行.
如果没有传入当前Promise异步成功,却没有传入onFulfilled或者异步失败,却没有传入onRejected函数的话,就直接resolve或者reject掉了。如果有传入,则先执行cb,将其结果值作为下一个deferred(也就是Handler,也就是当前Promise屁股后面的then啦)的newValue
这一段的实现,也就是为何我们能够使用如下代码,并拿到c啦
// 原谅我用个Promise.resolve, 写Promise实例要打好多字
// 不过`core`内没有Promise.resolve的实现
Promise.resolve('a')
.then(() => {
return new Promise((resolve) => {
setTimeout(() => {
resolve('b')
}, 100)
})
.then(() => 'c')
})
.then(result => console.log(result))
完结
哈,不是还有doResolve么,为何doResolve要用done标记啊。这个就留给大家仔细琢磨了。
夜深,明天补个总结图,晚安~










网友评论