美文网首页
then/promise简记

then/promise简记

作者: 博客专用马甲 | 来源:发表于2017-02-23 01:02 被阅读671次

问题

前段时间在使用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的值并没有被记录,只有调用了reslovevalue才能够记录在Promise实例的_value中。

resolve.png

源码理解

乍一眼看源码,有种晕晕的感觉。core里函数也不多,随手分了下类:

分类.png

考虑如下:

  1. Promise构造函数: 定义基本状态。暴露出来的then方法: 创建新的Promise
  2. 考虑日常调用: 由于Promise实例需要调用resolve/reject才能继续,所以resolve/reject符合回调模式,resolve/reject的后续调用应该是拿来改变各种Promise内部状态(突破口);
  3. Handler就三行,看上去是包装了一下onFulfilledonRejected,保留then所新建的Promise指针;
  4. 剩余的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;
  • 如果发现newValuePromise实例,当然是标注_stateadopt another promise,然后把_value指向新的Promise实例(newValue),再进入收尾函数finale;
  • 如果发现newValuefunction,就跟处理new Promise(fn)一样进入doResolve
  • 剩余newValue的情况无非就是NumberString等值了,此时当前异步已完成,修改状态_statefulfill,并记录_value值为newValue,再进入收尾函数finale.

reject就更简单了:

  • 更改状态_statereject,然后进入收尾函数finale.

收尾函数finale看上去好厉害哦,不晓得干了些什么事情.

收尾函数finale.png

根据resolvereject的处理逻辑,只有在

  1. newValuePromise实例;
  2. Number等正常值时;
    (进入doResolve线的最后还是要走这两条路子)
  3. 执行函数失败时;

才会进入finale. finale所做的事情是针对_deferredState的取值进入不同的处理。
根据之前的认知,_deferredState记录的是当前Promise后续有一个then还是多个then。结合代码来看其实很容易理解啦,就是将多个then逐一经过handle处理.

handle

一旦读懂_deferredState的作用,handle简直不在话下嘛。
调用handle函数只有两个地方(safeThenthen记为一处,另外是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标记啊。这个就留给大家仔细琢磨了。
夜深,明天补个总结图,晚安~

相关文章

网友评论

      本文标题:then/promise简记

      本文链接:https://www.haomeiwen.com/subject/ajkswttx.html