問題
前段時間在使用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ù)
可以發(fā)現(xiàn),由于我們的fn
并沒有調(diào)用resolve
糖权,所以res
的值并沒有被記錄堵腹,只有調(diào)用了reslove
,value
才能夠記錄在Promise
實例的_value
中星澳。
源碼理解
乍一眼看源碼疚顷,有種暈暈的感覺。core
里函數(shù)也不多募判,隨手分了下類:
考慮如下:
-
Promise
構(gòu)造函數(shù): 定義基本狀態(tài)荡含。暴露出來的then
方法: 創(chuàng)建新的Promise
咒唆; - 考慮日常調(diào)用: 由于
Promise
實例需要調(diào)用resolve/reject
才能繼續(xù),所以resolve/reject
符合回調(diào)模式释液,resolve
/reject
的后續(xù)調(diào)用應(yīng)該是拿來改變各種Promise
內(nèi)部狀態(tài)(突破口); -
Handler
就三行全释,看上去是包裝了一下onFulfilled
和onRejected
,保留then
所新建的Promise
指針误债; - 剩余的
handle/handleResolve/doResolve/finale
不是很好理解浸船,留待慢慢分析;
Promise構(gòu)造函數(shù)
結(jié)合作者注釋,可整理如下:
內(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
方法牲蜀,過程基本上是醬紫的:
resolve / reject
resolve
方法接受(self(當(dāng)前Promise實例), newValue(異步執(zhí)行結(jié)果))
笆制,干的事情基本符合猜想:
- 處理出錯(
newValue == self || getThen(newValue)失敗
)的情況: 拋鍋給reject
; - 如果發(fā)現(xiàn)
newValue
是Promise
實例,當(dāng)然是標(biāo)注_state
為adopt another promise
涣达,然后把_value
指向新的Promise實例(newValue
)在辆,再進入收尾函數(shù)finale
; - 如果發(fā)現(xiàn)
newValue
是function
,就跟處理new Promise(fn)
一樣進入doResolve
度苔; - 剩余
newValue
的情況無非就是Number
匆篓、String
等值了,此時當(dāng)前異步已完成林螃,修改狀態(tài)_state
為fulfill
奕删,并記錄_value
值為newValue
俺泣,再進入收尾函數(shù)finale
.
reject
就更簡單了:
- 更改狀態(tài)
_state
為reject
疗认,然后進入收尾函數(shù)finale
.
收尾函數(shù)finale
看上去好厲害哦,不曉得干了些什么事情.
根據(jù)resolve
和reject
的處理邏輯伏钠,只有在
-
newValue
為Promise
實例; -
Number
等正常值時;
(進入doResolve
線的最后還是要走這兩條路子) - 執(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ù)只有兩個地方(safeThen
和then
記為一處赴肚,另外是finale
)素跺。這兩塊地方代碼幾乎不重用二蓝。不是很理解為何在同一處進行處理。
首先理解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
啦)
稍微解釋下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é)圖,晚安~