then/promise简记

问题

前段时间在使用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标记啊。这个就留给大家仔细琢磨了。
夜深,明天补个总结图,晚安~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容