概述
自從準備晉級之後,就拖更了很久了,既然晉級弄完了,那麼也恢復更新了。
在面試別人的過程中,發現基本上沒有人對整個Promise完全瞭解,因此希望通過這篇文章來幫助大家瞭解下Promise的全貌。本文的主要內容是Promise/A+規範的譯文,主要是能夠幫助大家瞭解下Promise的規範,以及解決在日常運用中遇到的一些問題。
原文地址為:promisesaplus.com/#point-1
如果需要測試一個Promise庫是否符合Promise/A+規範,可以使用這個:github.com/promises-ap… 。
原文概覽
一個標準宣告、可操作的JavaScript Promise——出自開發者,為了開發者。
一個promise代表一個非同步操作的最終結果。與promise互動的主要方法是通過它的then
函式。這個函式註冊一個回撥函式來接收promise最終的值(value)或者是沒有成功的原因(reason)。
這篇文件詳細說明了then
方法的具體行為,它為所有的符合Promise/A+規範的實現提供了一個基礎的互動。像這樣,這個規範應該設計的非常穩定。儘管Promise/A+組織可能偶爾通過一些向後相容的小修改來調整這個文件來適應新發現的場景,我們只會在經歷過了小心的思考、討論和測試後增加大的、不向後相容的修改。
從歷史上來看,Promise/A+闡述了更早的Promise/A的行為規範,並且在其基礎上進行了擴充套件,覆蓋了一些實際行為和之前遺漏的問題。
最終,核心的Promise/A+文件不關心如何去建立、完成(resolve)或者拒絕(reject)一個Promise,而是聚焦在提供一個可互動的then
函式。在將來的其他規範中可能會涉及這些沒有提及的內容。
1. 術語
1.1. "promise"是一個物件或者函式,它擁有一個符合文件中描述行為的then
方法。
1.2. "thenable"是一個有then
方法的物件或者函式。
1.3. "value"是一個合法的JavaScript值(包括undefined
, 一個thenable或者一個promise)。
1.4. "exception"是一個使用在throw
語句中的丟擲來的值。
1.5. "reason"是一個用來表示promise拒絕的原因的值。
2. 要求
2.1 promise狀態
一個promise必須處於一下三種狀態:pending、fulfilled或者rejected。
2.1.1. 當處於pending狀態時,promise:
2.1.1.1. 可能會轉換成任何其他的狀態。
2.1.2. 當處於fulfilled狀態時,promise:
2.1.2.1. 禁止轉換成其他狀態。
2.1.2.2. 必須有一個無法更改的值。
2.1.3. 當處於rejected狀態時,promise:
2.1.3.1. 禁止轉換成其他狀態。
2.1.3.2. 必須有一個無法更改的原因。
在這裡,無法更改意味著全等(例如===
),但是不代表深比較相等。
2.2 then
方法
promise必須包含一個then
方法來訪問它當前或者最終的值或者原因。
Promise的then
方法接收兩個引數:
promise.then(onFulfilled, onRejected)
複製程式碼
2.1.1. onFulfilled
和onRejected
函式有都有可選的引數:
2.2.1.1. 如果onFulfilled
不是一個函式,那麼它必須被忽略掉。
2.2.1.2. 如果onRejected
不是一個函式,那麼它必須被忽略掉。
2.2.2. 如果onFulfilled
是一個函式:
2.2.2.1. 它必須在promise
到fulfilled狀態後觸發,promise
的值是它的第一個引數。
2.2.2.2. 它在一個promise
到fulfilled狀態之前禁止被觸發。
2.2.2.3. 它禁止被觸發多次。
2.2.3. 如果onRejected
是一個函式:
2.2.3.1. 它必須在promise
到rejected狀態後觸發,promise
的原因是它的第一個引數。
2.3.2.2. 它在一個promise
到rejected之前禁止被觸發。
2.3.2.3. 它禁止被觸發多次。
2.2.4. onFulfilled
或者onRejected
只有在執行上下文堆疊只有平臺程式碼時才能被觸發。
2.2.5. onFulfilled
和onRejected
必須作為函式被呼叫(例如沒有this
值)。
2.2.6. then
方法可能在相同的promise中被呼叫多次。
2.2.6.1. 如果promise
到了fullfilled狀態,那麼所有的onFulfilled
回撥函式都必須按照他們原有的順序進行呼叫執行。
2.2.6.2. 如果promise
到了rejected狀態,那麼所有的onRejected
回撥函式都必須按照他們原有的順序進行呼叫執行。
2.2.7. then
方法必須返回一個promise:
promise2 = promise1.then(onFulfilled, onRejected);
複製程式碼
2.2.7.1. 如果onFulfilled
或者onRejected
方法返回一個值x
,那麼執行promise的解析過程[[Resolve]](promise2, x)
。
2.2.7.2. 如果onFulfilled
或者onRejected
方法丟擲一個異常e
,promise2
必須使用e
作為原因拒絕掉(rejected)。
2.2.7.3. 如果onFulfilled
不是一個函式並且promise1
到了fulfilled狀態,那麼promise2
必須在與promise1
的值相同的情況下轉換到fulfilled狀態。
2.2.7.4. 如果onRejected
不是一個函式並且promise1
到了rejected狀態,那麼promise2
必須在與promise1
的原因相同的情況下轉換到rejected狀態。
2.3. promise解析函式
promise解析函式是一個輸入一個promise或者一個值的抽象的操作,我們表示為[[Resolve]](promise, x)
。如果x
是一個thenable物件,在假定x
的行為至少有點像一個promise的情況下,它會嘗試讓promise
轉換到x
的狀態。否則,他會用x
的值完成promise
的狀態。
這種thenable物件的方式允許promise實現互動,只要他們暴露一個符合Promise/A+規範的then
函式。它還允許Promise/A+的實現支援一個有合適的then
方法的不相容的實現。
執行[[Resolve]](promise, x)
,需要遵循以下步驟:
2.3.1. 如果promise
和x
指向同一個物件,那麼用TypeError
作為原因拒絕promise。
2.3.2. 如果x
是一個promise,判斷它的狀態:
2.3.2.1. 如果x
是pending狀態,promise
保留pending狀態直到x
變成fulfilled狀態或者rejected狀態。
2.3.2.2. 如果x
是fulfilled狀態,那麼用同樣的值將整個promise
完成。
2.3.2.3. 如果x
是rejected狀態,那麼用同樣的原因拒絕promise
。
2.3.3. 否則,如果x
是一個物件或者函式,
2.3.3.1. 讓then
變成x.then
。
2.3.3.2. 如果在檢測x.then
這個屬性的結果時丟擲一個異常e
,把e
作為原因拒絕promise
。
2.3.3.3. 如果then
是一個函式,那麼用x
作為this
來呼叫它,第一個引數是resolvePromise
,第二個引數是rejectPromise
:
2.3.3.3.1. 如果resolvePromise
被值y
呼叫,那麼執行[[Resolve]](promise, y)
。
2.3.3.3.2. 如果rejectPromise
被原因r
觸發,那麼用r
來拒絕promise
。
2.3.3.3.3. 如果resolvePromise
和rejectPromise
都被呼叫,或者使用相同的引數多次呼叫,那麼第一次呼叫生效,其他之後的任何呼叫都忽略掉。
2.3.3.3.4. 如果在呼叫then
方法時丟擲了一個異常e
,
2.3.3.3.4.1. 如果resolvePromise
和rejectPromise
已經被呼叫了那麼就忽略掉它。
2.3.3.3.4.2. 否則,使用e
作為原因拒絕promise
。
2.3.3.4. 如果then
不是一個函式,那麼用x
完成promise
。
2.3.4. 如果x
不是一個物件或者函式,那麼用x
完成promise
。
如果一個promise是通過在環形的thenable鏈中的一個thenable來完成的,如遞迴的[[Resolve]](promise, thenable)
型別再次呼叫[[Resolve]](promise, thenable)
,遵循上述的規則會導致無窮遞迴。對這種遞迴情況的檢測並且使用TypeError
作為原因進行拒絕,我們鼓勵實現,但不要求。
3. 注意事項
3.1. 在這裡"平臺程式碼"(platform code)意味著引擎,環境和promise實現程式碼。在實踐中,這個要求確保了onFulfilled
和onRejected
是非同步執行的,then
呼叫也是在迴圈之後,有一個新的堆疊資訊。這可以通過一個巨集任務(macro-task)機制例如setTimeout
或者setImmediate
來實現,也可以通過一個微任務(micro-task)例如MutationObserver
或者process.nextTick
來實現。如果promise的實現考慮平臺程式碼,那麼它自己可能會帶一個任務執行佇列或者“蹦床”來處理被呼叫情況。
3.2. 在嚴格模式下,this
是在promise裡面將會是undefined
。在鬆散模型下,他代表的是全域性物件。
3.3. 如果實現滿足所有要求的話,可以允許promise2 === promise1
。每一個實現都應該表明是否支援promise2 === promise1
,如果支援則是需要在什麼條件下。
3.4. 通常來說,如果按照當前的實現方式,我們只能知道x
是一個真的promise。這一條允許你在具體實現的使用過程中來判斷未知的promise的狀態。
3.5. 在程式中,首先儲存x.then
的引用,其次測試這個引用,然後再呼叫這個引用,避免多次訪問x.then
屬性。這樣的預防措施對於確保那些會在兩次訪問之間可能變化的屬性值獲取到一致的結果非常重要。
3.6. 實現中不應該對thenable呼叫鏈值設定任意深度限制,而是應該假設這個遞迴的限制值是無窮大。只有真正的迴圈才會導致TypeError
;如果遇到了一個長度為無窮大的不同的thenable,保證在正確的行為下一直遞迴。
總結
本文主要通過英文翻譯為中文的Promise/A+規範,讓大家瞭解了整個規範的全部內容。我會在下一篇部落格中給大家帶來如何實現一個完全符合Promise/A+規範的Promise。