第一次翻譯技術文章。 原文在此 ,另有一篇 Differences from Promises/A 未譯。
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。將來,本規範的同族規範則會涉及這些議題。
then
函數的對象或者函數,其行為符合本規範。 then
的對象或者函數。 undefined
,一個thenable對象,或者一個promise)。 throw
語句拋出的對象。 以下三種狀態,promise必居其一:pending, fulfilled, rejected。
此處所謂“不可更改”只是說變量的指針不可修改(滿足 ===
),并不是說該對象的屬性不可修改。
then
函數 promise必須提供一個 then
函數,來操作其產生的value或者reason。
then
函數接受2個參數:
promise.then(onFulfilled, onRejected)
onFulfilled
和 onRejected
是可選參數: onFulfilled
不是函數,則應該忽略之。 onRejected
不是函數,則應該忽略之。 onFulfilled
是函數: promise
被fullfill之後必須調用之,且以 promise
的value作為第一個參數。 promise
被fulfill之前,不得調用之。 onRejected
是函數, promise
被reject之後必須調用之,且以 promise
的reason作為第一個參數。 promise
被reject之前,不得調用之。 onFulfilled
和 onRejected
必須在 execution context 堆棧中只剩下platform code之後才能執行。[注釋1] onFulfilled
與 onRejected
必須作為函數調用。(不能帶 this
值)。[注釋2] then
可以在一個promise中被多次調用。 promise
轉為fulfilled狀態,所有通過 then
綁定的 onFulfilled
回調函數必須按照順序執行。 promise
轉為rejected狀態,所有通過 then
綁定的 onRejected
回調函數必須按照順序執行。 then
的返回值必須為promise[注釋3].
promise2 = promise1.then(onFulfilled, onRejected);
onFulfilled
和 onRejected
都會返回 x
,則執行Promise Resolution Procedure [[Resolve]](promise2, x)
。 onFulfilled
和 onRejected
都會拋出異常 e
, promise2
必須被reject,且以 e
作為reason。 onFulfilled
不是函數,並且 promise1
被fulfill,則 promise2
必須也被fulfill,其value與 promise1
相同。 onRejected
不是函數,並且 promise1
被reject,則 promise2
必須也被reject,其reason與 promise1
相同。 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)
的行為,需遵循以下步驟:
promise
與 x
指向同一個對象,用 TypeError
作為reason,reject這個 promise
。 x
是一個promise,則採用其狀態。[注釋4]: x
處於pending狀態,則 promise
必須保持pending狀態,直到 x
被fulfill或者reject。 x
被fulfill,用同樣的value來fulfill promise
。 x
被reject,用同樣的reason來reject promise
。 x
是一個對象或者函數, then
指向 x.then
。[注釋5] x.then
的時候捕獲異常 e
,則用 e
作為reason來reject這個 promise
。 then
是函數,則調用他,以 x
作為 this
,第一個參數為 resolvePromise
,第二個為 rejectPromise
,並且: resolvePromise
被執行,參數為 y
,則運行 [[Resolve]](promise, y)
。 rejectPromise
被執行,參數是 r
,則用 r
來reject這個 promise
。 resolvePromise
和 rejectPromise
都被調用,或者他們被重複調用,並且參數是一樣的,則只有第一個調用生效,其他被忽略。 then
時捕獲異常 e
, resolvePromise
或者 rejectPromise
已經執行了,則忽略之。 e
作為reason,reject該 promise
。 then
不是函數,以 x
為value,fulfill該 promise
。 x
不是對象或者函數,以 x
為value,fulfill該 promise
。 如果resolve的第二個參數是一個處在循環鏈中的thenable,其中的遞歸邏輯會導致 [[Resolve]](promise, thenable)
被反復調用,按照上面的算法,最終會陷入死循環。Promises/A+實現應該檢測這種循環關係,並且以 TypeError
作為reason,reject之。是為非強制性要求。[注釋6]
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.
That is, in strict mode this
will be undefined
inside of them; in sloppy mode, it will be the global object.
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.
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.
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.
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.