漢譯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來fulfillpromise
    3. 如果x被reject,用同樣的reason來rejectpromise
  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.

原文地址:https://www.cnblogs.com/jonkee/p/5092484.html