转载

漢譯Promises/A+規範

第一次翻譯技術文章。 原文在此 ,另有一篇 Differences from Promises/A 未譯。

Promises/A+

為Javascript的promises—by實現及其實現者所制定的開放標準,以期所有實現能達到健全一致的效果。

promise 表示異步操作的最終結果。與promise交互的主要方式是通過其 then 函數,註冊回調函數,從而對promise被執行後的value,或者被reject後的reason做進一步處理。

本規範定義了 then 函數的行為,以期提供一個藍本,讓所有遵循Promises/A+的promise的實現有本可依。故此,本規範應該保持穩定。即使Promises/A+組織可能偶爾修訂本規範,加入一些向後兼容的小修改,來覆蓋新發現的邊界問題,但我們只會在經過認真思考論證和測試之後,才會加入比較大的或者向後不兼容的修改。

Promises/A+繼承修改了早期 Promises/A proposal 的主要內容, 并根據實際( de facto )擴展了一些內容,還去掉了那些尚未成為正式規範,或者有問題的部分。

要之,Promises/A+規範旨在指導實現者如何提供一個 then 函數,而非如何create,fulfill,或者reject一個promises。將來,本規範的同族規範則會涉及這些議題。

術語

  1. "promise",一個具有 then 函數的對象或者函數,其行為符合本規範。
  2. "thenable",一個具有 then 的對象或者函數。
  3. "value",一個JavaScript對象(包括 undefined ,一個thenable對象,或者一個promise)。
  4. "exception",用 throw 語句拋出的對象。
  5. "reason",用以表示promise被reject的原因的值。

要素

promise的狀態

以下三種狀態,promise必居其一:pending, fulfilled, rejected。

  1. 於pending狀態,則promise:
    1. 可以轉換為fulfilled或者rejected狀態。
  2. 於fulfilled狀態,則promise:
    1. 不得再轉換為其他任何狀態。
    2. 必須有一個value,且不可更改。
  3. 於rejected狀態,則promise:
    1. 不得轉為其他狀態。
    2. 必須有一個reason,且不可更改。

此處所謂“不可更改”只是說變量的指針不可修改(滿足 === ),并不是說該對象的屬性不可修改。

then 函數

promise必須提供一個 then 函數,來操作其產生的value或者reason。

then 函數接受2個參數:

promise.then(onFulfilled, onRejected)
  1. onFulfilledonRejected 是可選參數:
    1. 如果 onFulfilled 不是函數,則應該忽略之。
    2. 如果 onRejected 不是函數,則應該忽略之。
  2. 如果 onFulfilled 是函數:
    1. promise 被fullfill之後必須調用之,且以 promise 的value作為第一個參數。
    2. promise 被fulfill之前,不得調用之。
    3. 只能調用一次。
  3. 如果 onRejected 是函數,
    1. promise 被reject之後必須調用之,且以 promise 的reason作為第一個參數。
    2. promise 被reject之前,不得調用之。
    3. 只能調用一次。
  4. onFulfilledonRejected 必須在 execution context 堆棧中只剩下platform code之後才能執行。[注釋1]
  5. onFulfilledonRejected 必須作為函數調用。(不能帶 this 值)。[注釋2]
  6. then 可以在一個promise中被多次調用。
    1. 如果 promise 轉為fulfilled狀態,所有通過 then 綁定的 onFulfilled 回調函數必須按照順序執行。
    2. 如果 promise 轉為rejected狀態,所有通過 then 綁定的 onRejected 回調函數必須按照順序執行。
  7. then 的返回值必須為promise[注釋3].

    promise2 = promise1.then(onFulfilled, onRejected);
    1. 如果 onFulfilledonRejected 都會返回 x ,則執行Promise Resolution Procedure [[Resolve]](promise2, x)
    2. 如果 onFulfilledonRejected 都會拋出異常 epromise2 必須被reject,且以 e 作為reason。
    3. 如果 onFulfilled 不是函數,並且 promise1 被fulfill,則 promise2 必須也被fulfill,其value與 promise1 相同。
    4. 如果 onRejected 不是函數,並且 promise1 被reject,則 promise2 必須也被reject,其reason與 promise1 相同。

The Promise Resolution Procedure

promise resolution procedure是一個抽象的操作,需要傳入兩個參數,promise和x,可表示為 [[Resolve]](promise, x) 。如果 x 是一個thenable,實際上 x 在某種程度上就是一個promise,此時應該嘗試用 x 的狀態來替換 promise 的狀態。否則,以 x 為value,fulfill這個 promise

這種處理方式具有一個好處,如果某個對象具有符合Promises/A+規範的 then 函數,他就可以跟promise進行交互。從而讓Promises/A+實現可以吸納(assimilate)不兼容Promises/A+規範但卻具有 then 函數的實現。

[[Resolve]](promise, x) 的行為,需遵循以下步驟:

  1. 如果 promisex 指向同一個對象,用 TypeError 作為reason,reject這個 promise
  2. 如果 x 是一個promise,則採用其狀態。[注釋4]:
    1. 如果 x 處於pending狀態,則 promise 必須保持pending狀態,直到 x 被fulfill或者reject。
    2. 如果 x 被fulfill,用同樣的value來fulfill promise
    3. 如果 x 被reject,用同樣的reason來reject promise
  3. 此外,如果 x 是一個對象或者函數,
    1. 使 then 指向 x.then 。[注釋5]
    2. 如果獲取 x.then 的時候捕獲異常 e ,則用 e 作為reason來reject這個 promise
    3. 如果 then 是函數,則調用他,以 x 作為 this ,第一個參數為 resolvePromise ,第二個為 rejectPromise ,並且:
      1. 如果 resolvePromise 被執行,參數為 y ,則運行 [[Resolve]](promise, y)
      2. 如果 rejectPromise 被執行,參數是 r ,則用 r 來reject這個 promise
      3. 如果 resolvePromiserejectPromise 都被調用,或者他們被重複調用,並且參數是一樣的,則只有第一個調用生效,其他被忽略。
      4. 如果調用 then 時捕獲異常 e
        1. 如果 resolvePromise 或者 rejectPromise 已經執行了,則忽略之。
        2. 否則,用 e 作為reason,reject該 promise
    4. 如果 then 不是函數,以 x 為value,fulfill該 promise
  4. 如果 x 不是對象或者函數,以 x 為value,fulfill該 promise

如果resolve的第二個參數是一個處在循環鏈中的thenable,其中的遞歸邏輯會導致 [[Resolve]](promise, thenable) 被反復調用,按照上面的算法,最終會陷入死循環。Promises/A+實現應該檢測這種循環關係,並且以 TypeError 作為reason,reject之。是為非強制性要求。[注釋6]

Notes

  1. Here "platform code" means engine, environment, and promise implementation code. In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack. This can be implemented with either a "macro-task" mechanism such as setTimeout or setImmediate , or with a "micro-task" mechanism such as MutationObserver or process.nextTick . Since the promise implementation is considered platform code, it may itself contain a task-scheduling queue or "trampoline" in which the handlers are called.

  2. That is, in strict mode this will be undefined inside of them; in sloppy mode, it will be the global object.

  3. Implementations may allow promise2 === promise1 , provided the implementation meets all requirements. Each implementation should document whether it can produce promise2 === promise1 and under what conditions.

  4. Generally, it will only be known that x is a true promise if it comes from the current implementation. This clause allows the use of implementation-specific means to adopt the state of known-conformant promises.

  5. This procedure of first storing a reference to x.then , then testing that reference, and then calling that reference, avoids multiple accesses to the x.then property. Such precautions are important for ensuring consistency in the face of an accessor property, whose value could change between retrievals.

  6. Implementations should not set arbitrary limits on the depth of thenable chains, and assume that beyond that arbitrary limit the recursion will be infinite. Only true cycles should lead to a TypeError ; if an infinite chain of distinct thenables is encountered, recursing forever is the correct behavior.

正文到此结束
Loading...