Promises/A+規範

翻譯前言:搜索已經有多篇Promises/A+規範的中文翻譯,可參見[4],[5]。但[4][5]翻譯都難以理解,例如[4]中未保留原規範的章節編號,不容易對比原文。[4]中將reason翻譯爲"據因",但我理解他想要表達的是「拒因」,有一些文字中用的是「拒因」,但這樣翻譯很不容易理解。再好比[4]中將fulfilled狀態翻譯爲執行態, 後面中文語句中也很難以理解。[5]中儘量地將一些專用詞彙沒有翻譯爲中文,但錯別字較多,行文不順暢。本文參考了[4]與[5]的中文翻譯,文中強調的must、must not突出翻譯了出來,但儘量保留了原規範的章節編號,術語、狀態和一些特定詞彙不翻譯爲中文,直接採用英語反倒使得文字簡潔易懂。html

下面是Promises/A+規範Version 1.1.1 /2014-05-05的中文翻譯。前端


一個開放、健全且通用的 JavaScript Promise 標準。由開發者制定,供開發者參考。
git

Promise 表示一個異步操做的最終結果,與之進行交互的方式主要是 then 方法,該方法註冊了兩個回調函數,用於接收 promise 的最終值(eventual value)或promise 不能fulfilled的緣由(reason)。github

本規範詳細描述then方法的行爲特色,全部遵循 Promises/A+ 規範實現的 promise 都可以本標準做爲參照基礎來實現then方法。規範應當是十分穩定的。Promise/A+ 組織會不斷修訂本規範,以解決一些新發現的邊邊角角問題,但所作的改動會是微小且向後兼容的。若是咱們要進行大規模或者向後不兼容的更新,咱們必定會在事先進行謹慎地考慮、詳盡的探討和嚴格的測試。web

從歷史上說,本規範其實是澄清了以前 Promise/A 規範 中建議的行爲語句,擴展了這些建議中已成爲事實上規範的行爲,同時刪減了原規範中一些不足以成爲規範和有問題的部分。算法

最後,Promises/A+ 規範的核心不涉及如何建立、fulfill或reject promise,而是專一於提供一個可互操做的then方法。上述對於 promises 的操做方法未來在其餘規範中可能會說起。api

1. 術語

1.1 "promise" 是一個擁有 then 方法的對象或函數,該方法的行爲遵循本規範.promise

1.2「thenable」是一個定義了 then 方法的對象或函數.app

1.3「value」指任何 JavaScript 合法值(包括 undefined , thenable對象或promise對象).webapp

1.4「exception」是使用 throw 語句拋出的一個value。

1.5「reason」是表示promise狀態爲什麼轉換爲rejected的一個value.

2. 需求

2.1. Promise狀態

    Promise狀態必須爲pendingfulfilledrejected中的一種。

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.2.1. 必定不能轉換爲其餘狀態。

       2.1.2.2. 必須有一個reason,且必須不可改變。

    上述"必須不可改變「指的是恆等不變(immutable identity:便可用 === 判斷相等),而不是意味着深不可變(deep immutability。譯者注: 這裏須要再研究下)。

2.2. then方法

    promise必須提供一個then方法,用於獲取promise當前/最終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時必須被調用,promise的value做爲第一個參數。

      2.2.2.2. 當promise狀態爲fulfilled以前必定不能被調用。

      2.2.2.3. 調用次數必定不能超過一次。

2.2.3.  若是 onRejected 是函數:

      2.2.3.1. 當promise狀態爲rejected時必須被調用,promise的reason做爲第一個參數。

      2.2.3.2. 當promise狀態轉換爲rejected以前必定不能被調用。

      2.2.3.3. 調用次數必定不能超過一次

2.2.4.  onFulfilled 和 onRejected 只有當執行上下文堆棧中僅包含平臺代碼[3.1]時才能夠被調用。

2.2.5.  onFulfilled 和 onRejected 必須做爲函數來調用(即沒有 this 值)[3.2]

2.2.6.  對同一個promise能夠調用屢次then方法,

      2.2.6.1. 若是/當promise狀態爲fulfilled時,必須按調用then方法的順序依次執行 onFulfilled 回調函數。

      2.2.6.2. 若是/當promise狀態爲rejected時,必須按調用then方法的順序依次執行 onRejected 回調函數。 

2.2.7. then方法必須返回一個promise[3.3]

promise2 = promise1.then(onFulfilled, onRejected);

      2.2.7.1. 若是 onFulfilled 或 onRejected 函數返回值爲x,那麼執行Promise resolution過程 [[Resolve]](promise2, x) 。

      2.2.7.2. 若是 onFulfilled 或 onRejected 函數拋出異常e,那麼promise2狀態爲rejected, e 做爲reason。

      2.2.7.3. 若是 onFulfilled 不是函數且promise1狀態是fulfilled,那麼promise2狀態必須是fulfilled且與promise1的value相同。

      2.2.7.4. 若是 onRejected不是函數且promise1狀態是rejected,那麼promise2狀態必須是rejected且與promise1的reason相同。

2.3. Promise resolution過程

     Promise resolution過程是一個抽象操做,須要輸入一個promise和一個value,用 [[Resolve]](promise, x) 來表示。若是x是thenable對象,則嘗試使promise接受x的狀態,這裏假定x的行爲特性至少相似於一個promise;不然用value x來fulfill promise。

       這樣處理then對象可使promise的實現有更好的互操做: 只要它們暴露出一個遵循Promises/A+規範的then方法。這樣處理也使得遵循Promise/A+ 規範的實現能夠與那些未完整遵循規範但then方法合理的實現能良好共存。

     [[Resolve]](promise, x) 按照下面步驟來運行:

2.3.1. 若是promise和x指向同一個對象,則reject promise而且用一個TypeError做爲reason。

2.3.2. 若是x是一個promise實例,則以x的狀態做爲promise的狀態[3.4]

  2.3.2.1. 若是x的狀態爲pending,那麼promise的狀態必須保持爲pending,直到x的狀態變爲fulfilled或者rejected。

      2.3.2.2. 若是/當x的狀態爲fulfilled,則用一樣的value來fulfill promise。

      2.3.2.3. 若是/當x的狀態爲rejected,則用一樣的reason來reject promise

2.3.3. 不然,若是x是一個對象或函數

      2.3.3.1. 將x.then賦值給then[3.5]

      2.3.3.2. 若是在獲取x.then屬性時拋出一個異常e,則用e做爲reason來reject promise

      2.3.3.3. 若是then是函數類型,則以x做爲then函數內部的this指針,以resolvePromise爲第一個參數,rejectPromise爲第二個參數,調用then函數。這裏:

        2.3.3.3.1. 若是/當resolvePromise被調用且value爲y,則執行[[Resolve]](promise, y) 。

            2.3.3.3.2. 若是/當rejectPromise被調用且reason爲r,則用r來reject promise。

            2.3.3.3.3. 若是 resolvePromise 和 rejectPromise都被調用,或者屢次調用的參數都相同,則優先採用首次調用並忽略剩下的調用。

            2.3.3.3.4. 若是調用then方法拋出一個異常e,

      2.3.3.4.1. 若是resolvePromise或rejectPromise已經被調用,則忽略該異常。

      2.3.3.4.2. 不然用e做爲reason來reject promise。

       2.3.3.4. 若是then不是函數類型,用x來fulfill promise

2.3.4.若是x不是對象或函數,用x來fulfill promise

    若是一個promise是被一個循環的 thenable 鏈中的thenable對象resolve,而 [[Resolve]](promise, thenable) 的遞歸性質又使得 [[Resolve]](promise, thenable) 被再次調用,根據上述算法將會陷入無限遞歸之中。本規範不強制要求,但鼓勵實現者檢測這樣的遞歸是否存在,若檢測到存在則以一個TypeError爲reason來reject promise[3.6]。

3. 備註

3.1. 這裏的「平臺代碼」是指引擎、執行環境和promise實現代碼。實踐中,這個需求是確保 onFulfilled 和 onRejected 函數爲異步執行,且應該在 then 方法被調用的那一輪事件循環以後,在一個新的堆棧中執行。這個事件隊列能夠採用「宏任務(macro-task)」機制(諸如經過 setTimeout 或 setImmediate )或者「微任務(micro-task)」機制(諸如經過 MutationObserver 或 process.nextTick )來實現。因爲 promise 的實現代碼被視爲平臺代碼,所以實現代碼自身能夠包含一個任務調度隊列或者handler被調用的跳板


譯者注: 這裏說起了 macro-task 和 micro-task 兩個概念,可參見[6], [7]。當掛起任務時,JS 引擎會將全部任務按照類別分到這兩個隊列中,事件循環的每一輪都要處理來自 macro-task 隊列(這個隊列在 WHATWG specification中被稱爲任務隊列)的一個任務,該任務執行完畢後取出 micro-task 隊列中的全部任務順序執行;以後再是下一輪取出一個macro-task 任務,取出全部的micro-tasks,從而周而復始。

但這樣可能會致使當micro-task 隊列中全部任務運行完成,執行下一個macro-task 任務時已通過去了很長時間,這會致使UI被阻塞等各類異常。Node.js中process.nextTick函數會在micro-tasks中排隊, 有一個內在保護機制process.maxTickDepth能夠避免長時間阻塞,這個值缺省爲1000, 當這個時間到後就再也不處理micro-tasks,而是開始處理下一個macro-task

一般, 當須要以同步方式來作異步訪問時使用micro-tasks(即當即執行這一micro任務),不然使用macro-tasks.

舉例:

macro-tasks: setTimeout, setInterval, setImmediate, I/O, UI rendering
micro-tasks: process.nextTick, Promises, Object.observe, MutationObserver


3.2. 在strict模式下this指針爲undefined,而在sloppy模式下this指針爲全局對象。

3.3. 代碼實如今知足全部需求點的狀況下能夠容許 promise2 === promise1 。每一個實現都要有文檔說明其是否容許以及在何種條件下容許 promise2 === promise1。

3.4. 通常來講,若是按照當前實現,只知道x是個真正的 promise 。這一規則容許那些特例實現 接受 遵循規範要求的Promises的狀態。

3.5.  這步咱們先是存儲了一個指向 x.then 的引用,接下來測試該引用,而後調用該引用,以免屢次訪問 x.then 屬性。這種預防措施確保了訪問該屬性的一致性,由於其值可能在檢索調用時被改變。

3.6. 實現不該當對thenale鏈的深度有任何限制, 不該當假定超過該限制就會無限遞歸。只有真正的循環遞歸才應能致使 TypeError 異常;若是一條無限長的鏈上有不一樣的thenable,那麼不斷遞歸就是正確的行爲。

參考資料:

[1] Promises/A+, https://promisesaplus.com/

[2] Differences from Promises/A, https://promisesaplus.com/differences-from-promises-a

[3] Conformant Implementations, https://promisesaplus.com/implementations

[4] Promise A+ 規範 中文翻譯, http://malcolmyu.github.io/malnote/2015/06/12/Promises-A-Plus/

[5] 前端翻譯:Promises/A+規範, 肥仔John, http://www.cnblogs.com/fsjohnhuang/p/4139172.html

[6] Difference between microtask and macrotask within an event loop context, http://stackoverflow.com/questions/25915634/difference-between-microtask-and-macrotask-within-an-event-loop-context

[7] Promise進階介紹+原生實現, http://wengeezhang.com/?p=11

相關文章
相關標籤/搜索