大家好,歡迎來到前端研習圈的今日分享。上期跟大家淺析了為什麼要了解 Promises/A+
規範
今天我們就來拆解一下 Promises/A+
,從規範入手,重塑對 Promise
的理解, 接下來直接進入正題~
什麼是 Promise
首先,我們看一下規範
中如何定義 Promise
可以看到,規範中對 Promise 的描述是一個非同步操作的最終結果
。我們可以透過 then
方法註冊回撥來接收它的最終的 value
或者失敗的 reason
。
這也是為什麼在很多有關 Promise 的介紹中把它稱為 狀態機
的原因。由此可見,規範也並非處處都是晦澀難懂的黑話,定義還是非常簡潔明瞭的。
開放的標準
同時 A+
規範還是很開放的一套標準,在前言中,有這樣一段描述
規範並沒有約束 建立
一個 Promise 的方式,也沒有限制將一個 Promise 的狀態修改為 成功
或者 失敗
的方式,而是選擇聚焦於提供一個可互用的 then
方法。
如何理解這個 可互用
呢,其實也很簡單,假設有兩個基於 Promises/A+
實現 A
和 B
,針對 A
建立的 promise 的 then 處理邏輯,放在 B
上也同樣適用,這也就意味著,這套規範會為 then
增加很多約束條件,以實現 可互用
。
實際上也確實如此,A+
中大部分的條例其實都是在規定 then
方法應該如何實現
約定俗成的術語
在進入 A+
的為 Promise
制定的需求列表之前,我們先了解一下其中的 術語
關於 promise
和 thenable
的描述大家可能會有點疑惑,為什麼都是一個具備 then
方法的物件
(函式本質上也是物件),卻要叫兩個名字? 其實很好解釋,promise
是官方實現的叫法,而 thenable
是非官方實現的統稱(社群實現的叫法)
value
, reason
, exception
規範中說得很明確,這裡就不做多餘的闡述了
狀態的處理細則
規範中規定了一個 promise 的狀態必須為 pending
,fulfilled
,rejected
三者之一
處在 pending
時, 可以變為 fulfilled
,rejected
兩者之一
變為 fulfilled
時,狀態不能再改變,同時必須產出 value
,且 value
也不能被改變
變為 rejected
時,狀態不能再改變,同時必須產出 reason
,且 reason
也不能被改變
說簡單一點,promise 的狀態只能被改變一次,要麼成功要麼失敗, 有點不成功便成仁的味道了。
then 方法
關於 then
描述中, 規定了它需要接收兩個引數
這裡onFulfilled
和 onRejected
也就是我們日常說的 成功的回撥 和 失敗的回撥
約束條例
onFulfilled
和 onRejected
不是函式的場景將直接被忽略
當 onFulfilled
是一個函式,它必須在 promise 變為 fulfilled
後呼叫,同時把 value
作為引數,且只能呼叫一次
當 onRejected
是一個函式,它必須在 promise 變為 rejected
後呼叫,同時把 reason
作為引數,且只能呼叫一次
onFulfilled
和 onRejected
必須在平臺程式碼的執行上下文結束之後呼叫。
這句話怎麼理解呢,假設在 executor
中同步執行了 resolve
或 reject
,成功/失敗的回撥也必須等同步邏輯全部執行完後,才能執行。這也就是為什麼 onFulfilled
和 onRejected
會被放置在 micro task
中執行,而不是立即執行的原因
const p = new Promise((resolve, reject) => {
resolve(3)
console.log(1)
})
console.log(2)
p.then((value) => {
console.log(value)
})
// 輸出 1 2 3
onFulfilled
和 onRejected
必須作為普通函式呼叫,也就是說不考慮內部 this 的指向
當 then
在同一個 promise 上被呼叫多次,所註冊的回撥需要在 promise 變為 fulfilled
或 rejected
後,按照註冊的順序執行
then
方法的返回值,必須是一個新的 promise(promise2)
,這也就是 promise 能夠被 鏈式呼叫
的原因
onFulfilled
和 onRejected
的返回值 x
,需要交給一個名為 Promise Resolution Procedure
的流程去處理,主要是考慮 x
還是一個 promise 的場景,因此需要設計一個遞迴
流程來處理,這個流程的條例在下文會講
onFulfilled
和 onRejected
如果執行報錯,丟擲異常 e
,那麼 新返回的promise2
的狀態將變為 rejected
當 promise1 狀態變為 fulfilled
且 onFulfilled
不是一個函式時,promise2 將以相同的 value
變為 fulfilled
當 promise1 狀態變為 rejected
且 onRejected
不是一個函式時,promise2 將以相同的 reason
變為 rejected
Promise Resolution Procedure
最後就是上文中提及的 Promise Resolution Procedure
流程,這是一個抽象出來的概念並非某個具體實現。它會接收一個兩個引數,一個 promise
和一個 x
這裡規範中提到,當 x
是一個 thenable
時, 會嘗試讓 promise
使用 x
最終的狀態
看個例子吧
結合例子就不難發現,這樣做的意義是保證 then
方法返回的 p2
的狀態是由 p1
的 onFulfilled
或 onRejected
的返回值的狀態決定的。
接下來我們來看整體流程的條例
當promise
和 x
的引用是同一個物件,則將 promise 置為 rejected
並丟擲 TypeError
作為失敗的 reason
也就是如下場景
當 x
是一個 promise
,則會根據其狀態做不同的處理
如果 x
處於 pending
,那麼 promise
需要等 x
成功或失敗後再同步其狀態;如果 x
已經成功或失敗,那麼 promise
則直接同步其狀態
當 x
是一個普通物件或者函式時,這部分場景是 A+
中最複雜的部分,考慮到單純用文字描述可能會很抽象,這部分將放置在下期的 原始碼實現篇
中著重拆解
有能力的同學可以嘗試直接原文
當 x
既不是一個普通物件也不是函式時,promise
和 x
都將直接被置位 fulfilled
以上就是 Promises/A+
規範中全部條例了
結尾
經過對規範的拆解後,相信大家對 promise
的認識又有了不小的提升。不過還保留了一個大坑等著下期來補(條例2.3.3)
。下期我們將從 規範 -> 實現
來切實的感受 promise 的工作流程
最後希望大家每天進步一點點,我們下期見~