【翻譯】Promises/A+規範
英文原文:Promise/A+
我的部落格:Promise A+ 規範
譯者序:一年前曾譯過 Promise/A+ 規範,適時完全不懂 Promise 的思想,純粹將翻譯的過程當作學習,舊文譯下來詰屈聱牙,讀起來十分不順暢。誰知這樣一篇拙譯,一年之間竟然點選數千,成為谷歌搜尋的頭條。今日在理解之後重譯此規範,以饗讀者。
一個開放、健全且通用的 JavaScript Promise 標準。由開發者制定,供開發者參考。
譯文術語
- 解決(fulfill):指一個 promise 成功時進行的一系列操作,如狀態的改變、回撥的執行。雖然規範中用
fulfill
來表示解決,但在後世的 promise 實現多以resolve
來指代之。 - 拒絕(reject):指一個 promise 失敗時進行的一系列操作。
- 終值(eventual value):所謂終值,指的是 promise 被解決時傳遞給解決回撥的值,由於 promise 有一次性的特徵,因此當這個值被傳遞時,標誌著 promise 等待態的結束,故稱之終值,有時也直接簡稱為值(value)。
- 據因(reason):也就是拒絕原因,指在 promise 被拒絕時傳遞給拒絕回撥的值。
Promise 表示一個非同步操作的最終結果,與之進行互動的方式主要是 then
方法,該方法註冊了兩個回撥函式,用於接收 promise 的終值或本 promise 不能執行的原因。
本規範詳細列出了 then
方法的執行過程,所有遵循 Promises/A+ 規範實現的 promise 均可以本標準作為參照基礎來實施 then
方法。因而本規範是十分穩定的。儘管 Promise/A+ 組織有時可能會修訂本規範,但主要是為了處理一些特殊的邊界情況,且這些改動都是微小且向下相容的。如果我們要進行大規模不相容的更新,我們一定會在事先進行謹慎地考慮、詳盡的探討和嚴格的測試。
從歷史上說,本規範實際上是把之前 Promise/A 規範 中的建議明確成為了行為標準:我們一方面擴充套件了原有規範約定俗成的行為,一方面刪減了原規範的一些特例情況和有問題的部分。
最後,核心的 Promises/A+ 規範不設計如何建立、解決和拒絕 promise,而是專注於提供一個通用的 then
方法。上述對於 promises 的操作方法將來在其他規範中可能會提及。
術語
Promise
promise 是一個擁有 then
方法的物件或函式,其行為符合本規範;
thenable
是一個定義了 then
方法的物件或函式,文中譯作“擁有 then
方法”;
值(value)
指任何 JavaScript 的合法值(包括 undefined
, thenable 和 promise);
異常(exception)
是使用 throw
語句丟擲的一個值。
據因(reason)
表示一個 promise 的拒絕原因。
要求
Promise 的狀態
一個 Promise 的當前狀態必須為以下三種狀態中的一種:等待態(Pending)、執行態(Fulfilled)和拒絕態(Rejected)。
等待態(Pending)
處於等待態時,promise 需滿足以下條件:
- 可以遷移至執行態或拒絕態
執行態(Fulfilled)
處於執行態時,promise 需滿足以下條件:
- 不能遷移至其他任何狀態
- 必須擁有一個不可變的終值
拒絕態(Rejected)
處於拒絕態時,promise 需滿足以下條件:
- 不能遷移至其他任何狀態
- 必須擁有一個不可變的據因
這裡的不可變指的是恆等(即可用 ===
判斷相等),而不是意味著更深層次的不可變(譯者注:蓋指當 value 或 reason 不是基本值時,只要求其引用地址相等,但屬性值可被更改)。
Then 方法
一個 promise 必須提供一個 then
方法以訪問其當前值、終值和據因。
promise 的 then
方法接受兩個引數:
promise.then(onFulfilled, onRejected)
引數可選
onFulfilled
和 onRejected
都是可選引數。
- 如果
onFulfilled
不是函式,其必須被忽略 - 如果
onRejected
不是函式,其必須被忽略
onFulfilled
特性
如果 onFulfilled
是函式:
- 當
promise
執行結束後其必須被呼叫,其第一個引數為promise
的終值 - 在
promise
執行結束前其不可被呼叫 - 其呼叫次數不可超過一次
onRejected
特性
如果 onRejected
是函式:
- 當
promise
被拒絕執行後其必須被呼叫,其第一個引數為promise
的據因 - 在
promise
被拒絕執行前其不可被呼叫 - 其呼叫次數不可超過一次
呼叫時機
onFulfilled
和 onRejected
只有在執行環境堆疊僅包含平臺程式碼時才可被呼叫 注1
呼叫要求
onFulfilled
和 onRejected
必須被作為函式呼叫(即沒有 this
值)注2
多次呼叫
then
方法可以被同一個 promise
呼叫多次
- 當
promise
成功執行時,所有onFulfilled
需按照其註冊順序依次回撥 - 當
promise
被拒絕執行時,所有的onRejected
需按照其註冊順序依次回撥
返回
then
方法必須返回一個 promise
物件 注3
promise2 = promise1.then(onFulfilled, onRejected);
- 如果
onFulfilled
或者onRejected
返回一個值x
,則執行下面的 Promise 解決過程:[[Resolve]](promise2, x)
- 如果
onFulfilled
或者onRejected
丟擲一個異常e
,則promise2
必須拒絕執行,並返回拒因e
- 如果
onFulfilled
不是函式且promise1
成功執行,promise2
必須成功執行並返回相同的值 - 如果
onRejected
不是函式且promise1
拒絕執行,promise2
必須拒絕執行並返回相同的據因
譯者注:理解上面的“返回”部分非常重要,即:不論 promise1
被 reject 還是被 resolve 時 promise2
都會被 resolve,只有出現異常時才會被 rejected。
Promise 解決過程
Promise 解決過程是一個抽象的操作,其需輸入一個 promise
和一個值,我們表示為 [[Resolve]](promise, x)
,如果 x
有 then
方法且看上去像一個 Promise ,解決程式即嘗試使 promise
接受 x
的狀態;否則其用 x
的值來執行 promise
。
這種 thenable 的特性使得 Promise 的實現更具有通用性:只要其暴露出一個遵循 Promise/A+ 協議的 then
方法即可;這同時也使遵循 Promise/A+ 規範的實現可以與那些不太規範但可用的實現能良好共存。
執行 [[Resolve]](promise, x)
需遵循以下步驟:
x
與 promise
相等
如果 promise
和 x
指向同一物件,以 TypeError
為據因拒絕執行 promise
x
為 Promise
如果 x
為 Promise ,則使 promise
接受 x
的狀態 注4:
- 如果
x
處於等待態,promise
需保持為等待態直至x
被執行或拒絕 - 如果
x
處於執行態,用相同的值執行promise
- 如果
x
處於拒絕態,用相同的據因拒絕promise
x
為物件或函式
如果 x
為物件或者函式:
- 把
x.then
賦值給then
注5 - 如果取
x.then
的值時丟擲錯誤e
,則以e
為據因拒絕promise
- 如果
then
是函式,將x
作為函式的作用域this
呼叫之。傳遞兩個回撥函式作為引數,第一個引數叫做resolvePromise
,第二個引數叫做rejectPromise
:- 如果
resolvePromise
以值y
為引數被呼叫,則執行[[Resolve]](promise, y)
- 如果
rejectPromise
以據因r
為引數被呼叫,則以據因r
拒絕promise
- 如果
resolvePromise
和rejectPromise
均被呼叫,或者被同一引數呼叫了多次,則優先採用首次呼叫並忽略剩下的呼叫 - 如果呼叫
then
方法丟擲了異常e
:- 如果
resolvePromise
或rejectPromise
已經被呼叫,則忽略之 - 否則以
e
為據因拒絕promise
- 如果
- 如果
then
不是函式,以x
為引數執行promise
- 如果
- 如果
x
不為物件或者函式,以x
為引數執行promise
如果一個 promise 被一個迴圈的 thenable 鏈中的物件解決,而 [[Resolve]](promise, thenable)
的遞迴性質又使得其被再次呼叫,根據上述的演算法將會陷入無限遞迴之中。演算法雖不強制要求,但也鼓勵施者檢測這樣的遞迴是否存在,若檢測到存在則以一個可識別的 TypeError
為據因來拒絕 promise
注6。
註釋
注1 這裡的平臺程式碼指的是引擎、環境以及 promise 的實施程式碼。實踐中要確保
onFulfilled
和onRejected
方法非同步執行,且應該在then
方法被呼叫的那一輪事件迴圈之後的新執行棧中執行。這個事件佇列可以採用“巨集任務(macro-task)”機制或者“微任務(micro-task)”機制來實現。由於 promise 的實施程式碼本身就是平臺程式碼(譯者注:即都是 JavaScript),故程式碼自身在處理在處理程式時可能已經包含一個任務排程佇列。譯者注:這裡提及了 macrotask 和 microtask 兩個概念,這表示非同步任務的兩種分類。在掛起任務時,JS 引擎會將所有任務按照類別分到這兩個佇列中,首先在 macrotask 的佇列(這個佇列也被叫做 task queue)中取出第一個任務,執行完畢後取出 microtask 佇列中的所有任務順序執行;之後再取 macrotask 任務,周而復始,直至兩個佇列的任務都取完。
兩個類別的具體分類如下:
- macro-task: script(整體程式碼),
setTimeout
,setInterval
,setImmediate
, I/O, UI rendering - micro-task:
process.nextTick
,Promises
(這裡指瀏覽器實現的原生 Promise),Object.observe
,MutationObserver
詳見 stackoverflow 解答 或 這篇部落格
- macro-task: script(整體程式碼),
注2 也就是說在嚴格模式(strict)中,函式
this
的值為undefined
;在非嚴格模式中其為全域性物件。注3 程式碼實現在滿足所有要求的情況下可以允許
promise2 === promise1
。每個實現都要文件說明其是否允許以及在何種條件下允許promise2 === promise1
。注4 總體來說,如果
x
符合當前實現,我們才認為它是真正的 promise 。這一規則允許那些特例實現接受符合已知要求的 Promises 狀態。注5 這步我們先是儲存了一個指向
x.then
的引用,然後測試並呼叫該引用,以避免多次訪問x.then
屬性。這種預防措施確保了該屬性的一致性,因為其值可能在檢索呼叫時被改變。注6 實現不應該對 thenable 鏈的深度設限,並假定超出本限制的遞迴就是無限迴圈。只有真正的迴圈遞迴才應能導致
TypeError
異常;如果一條無限長的鏈上 thenable 均不相同,那麼遞迴下去永遠是正確的行為。
相關文章
- 【譯】 Promises/A+ 規範Promise
- 根據 Promises/A+規範 手寫 PromsiePromise
- 結合Promises/A+規範,深入解讀Promise(附原始碼)Promise原始碼
- 一步一步實現一個符合Promises/A+規範的PromisePromise
- Promises A+規範原文解讀 + es6實現(附詳細註釋)Promise
- Promise A+中文翻譯Promise
- 初始Promise/A+規範Promise
- [譯]前端基礎知識儲備——Promise/A+規範前端Promise
- 根據 Promises/A+ 手寫 PromsiePromise
- [翻譯] 使用JavaScript實現自己的PromisesJavaScriptPromise
- 一起學習造輪子(一):從零開始寫一個符合Promises/A+規範的promisePromise
- 手撕遵循 Promise/A+ 規範的 PromisePromise
- 基於promise /A+規範手寫promisePromise
- 實現一個Promise(基於Promise/A+規範)Promise
- 【翻譯】Python PEP8編碼規範(中文版)Python
- 按照 Promise/A+ 規範逐行註釋並實現 PromisePromise
- 手寫實現滿足 Promise/A+ 規範的 PromisePromise
- Promise的原始碼實現(完美符合Promise/A+規範)Promise原始碼
- 實現一個完美符合Promise/A+規範的PromisePromise
- 【譯】Android API 規範AndroidAPI
- 【譯】JavaScript中的PromisesJavaScriptPromise
- 手把手教你實現Promise(二)(基於Promise A+規範)Promise
- 從使用到原理,實現符合Promise A+規範的Promise方法Promise
- 手把手教你實現Promise(一)(基於Promise A+規範)Promise
- 【譯】Android NDK API 規範AndroidAPI
- Promise原理講解 && 實現一個Promise物件 (遵循Promise/A+規範)Promise物件
- 【譯】Async-Await≈Generators+PromisesAIPromise
- 一步步教你實現Promise/A+ 規範 完整版Promise
- 一步一步實現一個Promise A+規範的 PromisePromise
- 從手寫一個符合Promise/A+規範Promise來深入學習PromisePromise
- 一步步寫一個符合Promise/A+規範的庫Promise
- ES6 Promise - 讓我們解開的面紗(遵循Promise/A+規範)Promise
- 前端通訊:ajax設計方案(十)--- 完善Promise A+規範,增加mock資料功能前端PromiseMock
- MySQL資料庫規範 (設計規範+開發規範+操作規範)MySql資料庫
- 翻譯
- 規範與偏離規範
- 前端規範之HTML 規範前端HTML
- 前端規範之javascript規範前端JavaScript
- 前端規範之CSS規範前端CSS