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閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容