then/promise簡記

問題

前段時間在使用Promise的過程中遇到一個很疑惑的地方涨醋。大概是這樣的:

const p = new Promise((resolve, reject) => {
        return 'hello world'
}).then((result) => {
        // expected --> 'hello world'
        console.log(result)
})

結(jié)果并不能console出hello world江兢,也就是then里面的callback并沒有執(zhí)行到.

如果并非在Promise實例內(nèi)返回值颤难,而是resolve盒件,則可以console出hello world

const p = new Promise((resolve, reject) => {
        resolve('hello world')
}).then((result) => {
        // expected --> 'hello world'
        console.log(result)
})

一直想當(dāng)然以為new Promise(callback)then(callback)callback的使用是一樣的褪迟。略疑惑厕妖,于是去看了下then/promise的大致實現(xiàn)塘偎,發(fā)現(xiàn)和自己的思路完全不一樣胃碾,于是做下記錄雁佳。

解答

由于是被"bug"吸引過來的脐帝,所以第一時間看了Promise的構(gòu)造函數(shù)

Promise.png
doResolve.png

可以發(fā)現(xiàn),由于我們的fn并沒有調(diào)用resolve糖权,所以res的值并沒有被記錄堵腹,只有調(diào)用了reslovevalue才能夠記錄在Promise實例的_value中星澳。

resolve.png

源碼理解

乍一眼看源碼疚顷,有種暈暈的感覺。core里函數(shù)也不多募判,隨手分了下類:

分類.png

考慮如下:

  1. Promise構(gòu)造函數(shù): 定義基本狀態(tài)荡含。暴露出來的then方法: 創(chuàng)建新的Promise咒唆;
  2. 考慮日常調(diào)用: 由于Promise實例需要調(diào)用resolve/reject才能繼續(xù),所以resolve/reject符合回調(diào)模式释液,resolve/reject的后續(xù)調(diào)用應(yīng)該是拿來改變各種Promise內(nèi)部狀態(tài)(突破口);
  3. Handler就三行全释,看上去是包裝了一下onFulfilledonRejected,保留then所新建的Promise指針误债;
  4. 剩余的handle/handleResolve/doResolve/finale不是很好理解浸船,留待慢慢分析;

Promise構(gòu)造函數(shù)

結(jié)合作者注釋,可整理如下:


promise實例.png

內(nèi)部屬性解釋:

  • ** _state** (Int): 記錄的是自身的情況;
  • _deferredState (Int): 記錄的是當(dāng)前promise實例的then的情況;
  • _deferreds (Handler Array): 記錄的是當(dāng)前Promise實例之后的then(也就是Handler實例寝蹈,不理解稍后講解);
  • _value: 記錄的是當(dāng)前promise異步完得到的值李命,可能是實值也有可能是另一個promise.

_state保存異步情況, 其可能值如下:
0 : 獲取中;
1 : 獲取成功;
2 : 獲取被拒絕(失敗);
3: 需要獲取另外一個異步結(jié)果.

_deferredState看上去有點奇怪,其可能值如下:
0: 初始值;
1: 當(dāng)前Promise實例后續(xù)只有一個then的時候;
2: 當(dāng)前Promise實例后續(xù)有多個then的時候.

你一定要大叫: "什么鬼s锢稀封字!"
考慮這樣的情況

const p = new Promise(resolve => resolve('a'))

p.then((result) => {
        console.log(result) // 'a'
})

p.then((result) => {
        console.log(result) // 'a'
})

為何要這么記錄_deferredState呢?
因為handleResolved函數(shù)每次只處理一個deferred嘛.

then

唯一暴露出來的then方法, 做的事情是創(chuàng)建一個新的Promise實例耍鬓,并和當(dāng)前Promise實例進行_state_deferredState千絲萬縷的關(guān)聯(lián)阔籽。如果我們創(chuàng)建一個Promise實例,并多次調(diào)用then方法牲蜀,過程基本上是醬紫的:

then! then! then!.png

resolve / reject

resolve方法接受(self(當(dāng)前Promise實例), newValue(異步執(zhí)行結(jié)果))笆制,干的事情基本符合猜想:

  • 處理出錯(newValue == self || getThen(newValue)失敗)的情況: 拋鍋給reject;
  • 如果發(fā)現(xiàn)newValuePromise實例,當(dāng)然是標(biāo)注_stateadopt another promise涣达,然后把_value指向新的Promise實例(newValue)在辆,再進入收尾函數(shù)finale;
  • 如果發(fā)現(xiàn)newValuefunction,就跟處理new Promise(fn)一樣進入doResolve度苔;
  • 剩余newValue的情況無非就是Number匆篓、String等值了,此時當(dāng)前異步已完成林螃,修改狀態(tài)_statefulfill奕删,并記錄_value值為newValue俺泣,再進入收尾函數(shù)finale.

reject就更簡單了:

  • 更改狀態(tài)_statereject疗认,然后進入收尾函數(shù)finale.

收尾函數(shù)finale看上去好厲害哦,不曉得干了些什么事情.

收尾函數(shù)finale.png

根據(jù)resolvereject的處理邏輯伏钠,只有在

  1. newValuePromise實例;
  2. Number等正常值時;
    (進入doResolve線的最后還是要走這兩條路子)
  3. 執(zhí)行函數(shù)失敗時;

才會進入finale. finale所做的事情是針對_deferredState的取值進入不同的處理横漏。
根據(jù)之前的認(rèn)知,_deferredState記錄的是當(dāng)前Promise后續(xù)有一個then還是多個then。結(jié)合代碼來看其實很容易理解啦熟掂,就是將多個then逐一經(jīng)過handle處理.

handle

一旦讀懂_deferredState的作用缎浇,handle簡直不在話下嘛。
調(diào)用handle函數(shù)只有兩個地方(safeThenthen記為一處赴肚,另外是finale)素跺。這兩塊地方代碼幾乎不重用二蓝。不是很理解為何在同一處進行處理。

handle.png

首先理解while指厌,根據(jù)作者注釋刊愚,_state為3的意義即是: adopt another Promise,也就是這樣的情況:

        new   Promise(resolve => resolve('a'))
        .then(() => {
               return new Promise(resolve => resolve('b')) // 標(biāo)記
       })
       .then((result) => {
              console.log(result) // 'b'
      })

之前談到resolve的時候談到過如果newValue也是Promise實例或者是正常值踩验,都會被記錄到_value中鸥诽,此處代碼的意義也就是拿到標(biāo)記處異步的最終結(jié)果啦~

  • then或是safeThen進入: 此時self._state的值應(yīng)該還是0,通過判斷當(dāng)前Promise實例的后續(xù)個數(shù)箕憾,_deferreds收集到后續(xù)所有的deferred(其實就是Handler實例啦)牡借,// 講人話就是跟在當(dāng)前Promise屁股后面有多少個then
  • finale進入: 此時self._state的值實際上為1或者2,反正是處于解決的狀態(tài)袭异,為何不是3?因為前面while了嘛钠龙。此時不會經(jīng)過self._state === 0的判斷,而是直接走向handleResolved了 // 終于干正事了

handleResolve

handleResolve簡直是core代碼里面的高潮嘛~
這里做的處理是從當(dāng)前Promise實例過渡到下一個deferred(也就是Handler御铃,也就是當(dāng)前Promise屁股后面的then啦)

handleResolve.png

稍微解釋下asap俊鱼,看上去應(yīng)該是類似將當(dāng)前fn轉(zhuǎn)成microtask,在當(dāng)前event loop末尾執(zhí)行.

如果沒有傳入當(dāng)前Promise異步成功畅买,卻沒有傳入onFulfilled或者異步失敗并闲,卻沒有傳入onRejected函數(shù)的話,就直接resolve或者reject掉了谷羞。如果有傳入帝火,則先執(zhí)行cb,將其結(jié)果值作為下一個deferred(也就是Handler湃缎,也就是當(dāng)前Promise屁股后面的then啦)的newValue
這一段的實現(xiàn)犀填,也就是為何我們能夠使用如下代碼,并拿到c

// 原諒我用個Promise.resolve, 寫Promise實例要打好多字
// 不過`core`內(nèi)沒有Promise.resolve的實現(xiàn)
Promise.resolve('a')
        .then(() => {
                  return new Promise((resolve) => {
                        setTimeout(() => {
                              resolve('b')
                        }, 100)
                  })
                  .then(() => 'c')
        })
        .then(result => console.log(result))

完結(jié)

哈嗓违,不是還有doResolve么九巡,為何doResolve要用done標(biāo)記啊。這個就留給大家仔細(xì)琢磨了蹂季。
夜深冕广,明天補個總結(jié)圖,晚安~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末偿洁,一起剝皮案震驚了整個濱河市撒汉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌涕滋,老刑警劉巖睬辐,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡溯饵,警方通過查閱死者的電腦和手機侵俗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丰刊,“玉大人坡慌,你說我怎么就攤上這事≡迦” “怎么了洪橘?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長棵帽。 經(jīng)常有香客問我熄求,道長,這世上最難降的妖魔是什么逗概? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任弟晚,我火速辦了婚禮,結(jié)果婚禮上逾苫,老公的妹妹穿的比我還像新娘卿城。我一直安慰自己,他們只是感情好铅搓,可當(dāng)我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布瑟押。 她就那樣靜靜地躺著,像睡著了一般星掰。 火紅的嫁衣襯著肌膚如雪多望。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天氢烘,我揣著相機與錄音怀偷,去河邊找鬼。 笑死播玖,一個胖子當(dāng)著我的面吹牛椎工,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蜀踏,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼维蒙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了脓斩?” 一聲冷哼從身側(cè)響起木西,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤畴栖,失蹤者是張志新(化名)和其女友劉穎随静,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡燎猛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年恋捆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片重绷。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡沸停,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出昭卓,到底是詐尸還是另有隱情愤钾,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布候醒,位于F島的核電站能颁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏倒淫。R本人自食惡果不足惜伙菊,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望敌土。 院中可真熱鬧镜硕,春花似錦、人聲如沸返干。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽矩欠。三九已至念恍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間晚顷,已是汗流浹背峰伙。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留该默,地道東北人瞳氓。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像栓袖,于是被迫代替她去往敵國和親匣摘。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容