[譯]前端基礎知識儲備——Promise/A+規範

黃Java發表於2018-09-15

概述

自從準備晉級之後,就拖更了很久了,既然晉級弄完了,那麼也恢復更新了。

在面試別人的過程中,發現基本上沒有人對整個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. onFulfilledonRejected函式有都有可選的引數:

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. onFulfilledonRejected必須作為函式被呼叫(例如沒有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方法丟擲一個異常epromise2必須使用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. 如果promisex指向同一個物件,那麼用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. 如果resolvePromiserejectPromise都被呼叫,或者使用相同的引數多次呼叫,那麼第一次呼叫生效,其他之後的任何呼叫都忽略掉。

2.3.3.3.4. 如果在呼叫then方法時丟擲了一個異常e,

2.3.3.3.4.1. 如果resolvePromiserejectPromise已經被呼叫了那麼就忽略掉它。

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實現程式碼。在實踐中,這個要求確保了onFulfilledonRejected是非同步執行的,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。

相關文章