原文
An open standard for sound, interoperable JavaScript promises—by implementers, for implementers.
A promise represents the eventual result of an asynchronous operation. The primary way of interacting with a promise is through its then
method, which registers callbacks to receive either a promise’s eventual value or the reason why the promise cannot be fulfilled.
This specification details the behavior of the then
method, providing an interoperable base which all Promises/A+ conformant promise implementations can be depended on to provide. As such, the specification should be considered very stable. Although the Promises/A+ organization may occasionally revise this specification with minor backward-compatible changes to address newly-discovered corner cases, we will integrate large or backward-incompatible changes only after careful consideration, discussion, and testing.
Historically, Promises/A+ clarifies the behavioral clauses of the earlier Promises/A proposal, extending it to cover de facto behaviors and omitting parts that are underspecified or problematic.
Finally, the core Promises/A+ specification does not deal with how to create, fulfill, or reject promises, choosing instead to focus on providing an interoperable then
method. Future work in companion specifications may touch on these subjects.
Terminology
-
“promise” is an object or function with a
then
method whose behavior conforms to this specification. -
“thenable” is an object or function that defines a
then
method. -
“value” is any legal JavaScript value (including
undefined
, a thenable, or a promise). -
“exception” is a value that is thrown using the
throw
statement. - “reason” is a value that indicates why a promise was rejected.
Requirements
Promise States
A promise must be in one of three states: pending, fulfilled, or rejected.
Here, “must not change” means immutable identity (i.e. ===
), but does not imply deep immutability.
The then
Method
A promise must provide a then
method to access its current or eventual value or reason.
A promise’s then
method accepts two arguments:
promise.then(onFulfilled, onRejected)
onFulfilled
oronRejected
must not be called until the execution context stack contains only platform code. [3.1].onFulfilled
andonRejected
must be called as functions (i.e. with nothis
value). [3.2]-
then
must return a promise [3.3].promise2 = promise1.then(onFulfilled, onRejected);
-
If either
onFulfilled
oronRejected
returns a valuex
, run the Promise Resolution Procedure[[Resolve]](promise2, x)
. -
If either
onFulfilled
oronRejected
throws an exceptione
,promise2
must be rejected withe
as the reason. -
If
onFulfilled
is not a function andpromise1
is fulfilled,promise2
must be fulfilled with the same value aspromise1
. -
If
onRejected
is not a function andpromise1
is rejected,promise2
must be rejected with the same reason aspromise1
.
-
If either
The Promise Resolution Procedure
The promise resolution procedure is an abstract operation taking as input a promise and a value, which we denote as [[Resolve]](promise, x)
. If x
is a thenable, it attempts to make promise
adopt the state of x
, under the assumption that x
behaves at least somewhat like a promise. Otherwise, it fulfills promise
with the value x
.
This treatment of thenables allows promise implementations to interoperate, as long as they expose a Promises/A+-compliant then
method. It also allows Promises/A+ implementations to “assimilate” nonconformant implementations with reasonable then
methods.
To run [[Resolve]](promise, x)
, perform the following steps:
-
If
promise
andx
refer to the same object, rejectpromise
with aTypeError
as the reason. -
If
x
is a promise, adopt its state [3.4]: -
Otherwise, if
x
is an object or function,-
Let
then
bex.then
. [3.5] -
If retrieving the property
x.then
results in a thrown exceptione
, rejectpromise
withe
as the reason. -
If
then
is a function, call it withx
asthis
, first argumentresolvePromise
, and second argumentrejectPromise
, where:-
If/when
resolvePromise
is called with a valuey
, run[[Resolve]](promise, y)
. -
If/when
rejectPromise
is called with a reasonr
, rejectpromise
withr
. -
If both
resolvePromise
andrejectPromise
are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored. -
If calling
then
throws an exceptione
,
-
If/when
-
If
then
is not a function, fulfillpromise
withx
.
-
Let
-
If
x
is not an object or function, fulfillpromise
withx
.
If a promise is resolved with a thenable that participates in a circular thenable chain, such that the recursive nature of [[Resolve]](promise, thenable)
eventually causes [[Resolve]](promise, thenable)
to be called again, following the above algorithm will lead to infinite recursion. Implementations are encouraged, but not required, to detect such recursion and reject promise
with an informative TypeError
as the reason. [3.6]
Notes
-
Here “platform code” means engine, environment, and promise implementation code. In practice, this requirement ensures that
onFulfilled
andonRejected
execute asynchronously, after the event loop turn in whichthen
is called, and with a fresh stack. This can be implemented with either a “macro-task” mechanism such assetTimeout
orsetImmediate
, or with a “micro-task” mechanism such asMutationObserver
orprocess.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 beundefined
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 producepromise2 === 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 thex.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.
中文譯文
由開發(fā)者制定的開放、通用可信任的的promise標(biāo)準(zhǔn),供開發(fā)者參考
promise代表了一個異步操作的最終結(jié)果。promise最主要的交互方式是通過它的 then
方法卓囚,這個方法注冊了回調(diào)函數(shù)用來接受一個promise的最終value或者被reject的reason。
該規(guī)范詳細說明了then
方法的行為,為所有符合promise/A+規(guī)范實現(xiàn)的promise均可以本標(biāo)準(zhǔn)作為參照基礎(chǔ)來實施 then 方法艘虎。因為這個規(guī)則十分穩(wěn)定。盡管Promises / A +組織有時會通過向后兼容的微小更改來修訂此規(guī)范,以解決新發(fā)現(xiàn)的極端情況蔑水,但只有在仔細考慮,討論和測試之后扬蕊,我們才會集成大型或向后不兼容的更改搀别。
從歷史上看,Promises / A +闡明了較早Promises / A提案的行為條款尾抑,將其擴展為涵蓋事實上的行為歇父,并省略了未指定或有問題的部分蒂培。
最后,核心的Promises / A +規(guī)范不涉及如何create庶骄,fulfill或reject一個promise毁渗,而是選擇專注于提供可互操作的then
方法。 規(guī)范中的未來工作可能涉及這些主題单刁。
術(shù)語
要求
Promise 狀態(tài)
一個promise 必須處于以下三個狀態(tài)種的一種: pending檐春,fulfilled或rejecged
在這里骨望,“不能轉(zhuǎn)換”意味著恒等(可以用===判斷),但不適用于深層的改變(如果value或reason不為基本類型欣舵,可能會修改屬性值)擎鸠。
then
方法
promise必須提供一個then
方法來訪問它的當(dāng)前值或最終value/reason。
promise.then(onFulfilled, onRejected)
-
- 一定會在promise狀態(tài)變成rejected之后調(diào)用遣疯,promise的reason是它第一個參數(shù)雄可。
- 在promise狀態(tài)沒有變?yōu)閞ejected之前不會被調(diào)用。
- [](https://promisesaplus.com/#point-33)無法被調(diào)用超過一次另锋。
onFulfilled
和onRejected
只有在執(zhí)行環(huán)境堆棧僅包含平臺代碼時才可被調(diào)用
[3.1].onFulfilled
和onRejected
必須被用作函數(shù)調(diào)用 (i.e. with nothis
value). [3.2]-
then
方法必定返回一個promise [3.3].promise2 = promise1.then(onFulfilled, onRejected);
如果
onFulfilled
或onRejected
拋出一個一場e
,promise2
必定返回e作為一reason室梅。如果
onFulfilled
不是一個函數(shù)并且promise1
是fulfilled狀態(tài)戏仓,promise2
必定和promise1
有同樣的value疚宇。如果
onRejected
不是一個函數(shù)并且promise1
處于rejected狀態(tài),promise2
與promise1
有相同的rason值
Promise解決過程
promise 解決過程是一個抽象操作,它輸入一個promise和一個value赏殃,我們可以表示為 [[Resolve]](promise, x)
. 如果 x 有 then 方法且看上去像一個 Promise 敷待,解決程序即嘗試使 promise 接受 x 的狀態(tài);否則其用 x 的值來執(zhí)行 promise 仁热。
這種 thenable 的特性使得 Promise 的實現(xiàn)更具有通用性:只要其暴露出一個遵循 Promise/A+ 協(xié)議的 then 方法即可榜揖;這同時也使遵循 Promise/A+ 規(guī)范的實現(xiàn)可以與那些不太規(guī)范但可用的實現(xiàn)能良好共存。
運行 [[Resolve]](promise, x) 需遵循以下步驟
-
如果
promise
和x
指向同一個對象抗蠢,使用TypeError
來將promise該為rejected举哟。 - 如果 x 為 Promise ,則使 promise 接受 x 的狀態(tài) [3.4]:
-
如果
x
是一個對象或函數(shù)- 把 x.then 賦值給 then [3.5]
- 如果取 x.then 的值時拋出錯誤 e 秽褒,則以 e 為據(jù)因拒絕 promise
- 果 then 是函數(shù)壶硅,將 x 作為函數(shù)的作用域 this 調(diào)用之。傳遞兩個回調(diào)函數(shù)作為參數(shù)销斟,第一個參數(shù)叫做 resolvePromise 庐椒,第二個參數(shù)叫做 rejectPromise:
- 如果 then 不是函數(shù)窗宇,以 x 為參數(shù)執(zhí)行 promise
- 如果 x 不為對象或者函數(shù),以 x 為參數(shù)執(zhí)行 promise
如果一個 promise 被一個循環(huán)的 thenable 鏈中的對象解決特纤,而 [[Resolve]](promise, thenable) 的遞歸性質(zhì)又使得其被再次調(diào)用军俊,根據(jù)上述的算法將會陷入無限遞歸之中。算法雖不強制要求捧存,但也鼓勵施者檢測這樣的遞歸是否存在粪躬,若檢測到存在則以一個可識別的 TypeError 為據(jù)因來拒絕 promise[3.6]
Notes
-
Here “platform code” means engine, environment, and promise implementation code. In practice, this requirement ensures that
onFulfilled
andonRejected
execute asynchronously, after the event loop turn in whichthen
is called, and with a fresh stack. This can be implemented with either a “macro-task” mechanism such assetTimeout
orsetImmediate
, or with a “micro-task” mechanism such asMutationObserver
orprocess.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 beundefined
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 producepromise2 === 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 thex.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.