【譯】 Promises/A+ 規範

Yangfan發表於2019-01-25

一個開放標準,對於開發人員可互操作的 JavaScript 承諾

一個 promise 代表一個非同步操作的最終結果。主要的操作方式是通過呼叫 promise 的 then 方法,它接受的回撥函式接受 promise 成功的結果或失敗的原因

這個規範詳細的描述了 then 方法的行為,提供一個互操作基礎,所有符合 Promises/A+ 的都可以依賴這個標準實現。因此,該規範已經十分穩定。儘管 Promises/A+ 組織可能會偶爾修改以實現向後相容,我們也會整合這些大的或不能向後相容的改變,一起研究,討論,測試。

曾經, Promises/A+ 解釋了早期 PromisesA 提議的條款,擴充套件了事實上的行為和忽略了不標準和有問題的部分。

最終,Promises/A+ 規範並沒處理如何建立 fulfill,或 reject promise,而選擇了可互操作的 then 方法替代。在今後的工作中可能會考慮。

1. 術語

1.1 ‘promise’ 是一個有符合此標準的 then 方法的 objectfunction

1.2 ‘thenable’ 是 then 方法定義的 objectfunction

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 狀態時:

  • 2.1.1.1 可能會轉變為 fulfilled 或 rejected 狀態

2.1.2 fulfilled 狀態時:

  • 2.1.2.1 不能再狀態為任何其他狀態

  • 2.1.2.2 必須有一個 value,且不可改變

2.1.3 rejected 狀態時:

  • 2.1.3.1 不能再狀態為任何其他狀態

  • 2.1.3.2 必須有一個 reason,且不可改變

注:這裡 ‘不可改變’ 意思是不可變恆等(同理 === ),但不意味永遠不可變

2.2 then 方法

一個 promise 必須提供一個 then 方法,用來獲取當前或最終的 value 或 reason

一個 promise 的 then 方法接受兩個引數:

promise.then(onFulfilled, onRejected)

2.2.1 onFulfilled 和 onRejected 都是可選引數:

  • 2.2.1.1 如果 onFulfilled 不是函式,它會被忽略

  • 2.2.1.2 如果 onRejected 不是函式,它會被忽略

2.2.2 如果 onFulfilled 是一個函式:

  • 2.2.2.1 它一定是在 promise 是 fulfilled 狀態後呼叫,並且接受一個引數 value

  • 2.2.2.2 它一定是在 promise 是 fulfilled 狀態後呼叫

  • 2.2.2.3 它最多被呼叫一次

2.2.3 如果 onRejected 是一個函式:

  • 2.2.3.1 它一定在 promise 是 rejected 狀態後呼叫,並且接受一個引數 reason

  • 2.2.3.2 它一定在 promise 是 rejected 狀態後呼叫

  • 2.2.3.3 它最多被呼叫一次

2.2.4 onFulfilled 或 onRejected 只在執行環境堆疊只包含平臺程式碼之後呼叫 [3.1]

2.2.5 onFulfilled 和 onRejected 會作為函式形式呼叫 (也就是說,預設 this 指向 global,嚴格模式 undefined) [3.2]

2.2.6 在同一個 promise 例項中,then 可以鏈式呼叫多次

  • 2.2.6.1 如果或當 promise 轉態是 fulfilled 時,所有的 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,那麼它會以

[[Resolve]](promise2, x) 處理解析

  • 2.2.7.2 如果 onFulfilled 或 onRejected 裡丟擲了一個異常,那麼 promise2 必須捕獲這個錯誤(接受一個 reason 引數)

  • 2.2.7.3 如果 onFulfilled 不是一個函式,並且 promise1 狀態是 fulfilled,那麼 promise2 一定會接受到與 promse1 一樣的值 value

  • 2.2.7.4 如果 onRejected 不是一個函式,並且 promise1 狀態是 rejected,promise2 一定會接受到與 promise1 一樣的值 reason

2.3 Promise 處理程式

promise 處理程式是一個表現形式為 [[Resolve]](promise, x) 的抽象處理操作。如果 x 是 thenable 型別,它會嘗試生成一個 promise 處理 x,否則它將直接 resolve x

只要 then 方法符合 Promises/A+ 規則,那麼對 thenables 處理就允許實現可互操作(鏈式呼叫,層層傳遞下去)。它也允許對那些不符合 Promises/A+ 的 then 方法進行 “吸收”

[[Resolve]](promise, x) 的執行表現形式如下步驟:

2.3.1 如果返回的 promise1 和 x 是指向同一個引用(迴圈引用),則丟擲錯誤

2.3.2 如果 x 是一個 promise 例項,則採用它的狀態:

  • 2.3.2.1 如果 x 是 pending 狀態,那麼保留它(遞迴執行這個 promise 處理程式),直到 pending 狀態轉為 fulfilled 或 rejected 狀態

  • 2.3.2.2 如果或當 x 狀態是 fulfilled,resolve 它,並且傳入和 promise1 一樣的值 value

  • 2.3.2.3 如果或當 x 狀態是 rejected,reject 它,並且傳入和 promise1 一樣的值 reason

2.3.3 此外,如果 x 是個物件或函式型別

  • 2.3.3.1 把 x.then 賦值給 then 變數

  • 2.3.3.2 如果捕獲(trycatch)到 x.then 丟擲的錯誤的話,需要 reject 這個promise

  • 2.3.3.3 如果 then 是函式型別,那個用 x 呼叫它(將 thenthis 指向 x),第一個引數傳 resolvePromise ,第二個引數傳 rejectPromise:

    • 2.3.3.3.1 如果或當 resolvePromise 被呼叫並接受一個引數 y 時,執行[[Resolve]](promise, y)

    • 2.3.3.3.2 如果或當 rejectPromise 被呼叫並接受一個引數 r 時,執行 reject(r)

    • 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 已經被呼叫或以相同的引數多次呼叫的話嗎,優先第一次的呼叫,並且之後的呼叫全部被忽略(避免多次呼叫)

    • 2.3.3.4 如果 then 執行過程中丟擲了異常,

      • 2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已經被呼叫,那麼忽略異常

      • 2.3.3.3.4.2 否則,則 reject 這個異常

  • 2.3.3.4 如果 then 不是函式型別,直接 resolve x(resolve(x))

2.3.4 如果 x 即不是函式型別也不是物件型別,直接 resolve x(resolve(x))

如果被 resolve 的 promise 參與了 thenable 的迴圈鏈中,那麼可能會導致無限遞迴。我們鼓勵實現檢測這種無限遞迴的方法並且返回一個錯誤資訊,但並不是必須的 [3.6]

3. 備註

3.1 這裡的 “平臺程式碼”是指引擎,環境,和 promise 實現程式碼。實際上,這個要求確保 onFulfilled 和 onRejected 都在下一輪的事件迴圈中(一個新的棧)被非同步呼叫。可以用巨集任務,例如:setTimeoutsetImmediate 或者微任務,例如:MutationObseverprocess.nextTick 實現。 由於 promise 的實現被當做平臺程式碼,所以它本身可能包含一個任務佇列或 “trampoline” 的處理程式

3.2 這個 this 在嚴格模式下是 undefined,在寬鬆模式,指向 global 物件

3.3 具體的實現可以允許 promise2 和 promise1 絕對相等,要滿足所有要求。每一個處理 promise2 和 promise1 絕對相等的實現都要寫上文件標註

3.4 通常,只有它來自當前實現才可以判斷 x 是一個真正的 promise。 此條款允許採取已知符合 promise 標準實現的狀態

3.5 把 x.then 存起來,然後測試、呼叫這個引用,避免多次訪問 x.then 屬性。這麼做的原因是防止每次獲取 x.then 時,返回不同的情況(ES5getter 特性可能會產生副作用)

3.6 實現不應該武斷地限制 thenable 鏈的深度,假設超出限制的無限遞迴。只有真正的迴圈引用才會導致一個 TypeError 錯誤,如果遇到一個不同的無限遞迴 thenable 鏈,一直遞迴永遠是正確的行為

  • 本文僅代表原作者個人觀點,譯者不發表任何觀點
  • Markdown 檔案由譯者手動整理,如有勘誤,歡迎指正
  • 譯文和原文采用一樣協議,侵刪

來源:https://juejin.im/post/5c4b0423e51d4525211c0fbc

相關文章