原文地址:http://promisesaplus.com/
An open standard for sound, interoperable JavaScript promises—by implementers, for implementers.
Promise用来表示异步操作的最终结果,主要通过Promise的then
方法来完成跟Promise的交互。当调用then
方法的时候,就是给Promise注册了一些回调函数,用来接收Promise的最终结果或者Promise无法完成的原因。
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 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.
名词解释
promise
是一个拥有then
方法的对象或者函数,而且then
方法的行为符合这个文档的规定。thenable
是一个定义了then
方法的对象或者函数。value
是任何一个合法的JavaScript的中的值(包括undefined
,thenable
,promise
)。exception
是一个通过throw
语句抛出的值。reason
是一个用来表示promise
为何无法满足的值。
必要条件
Promise的状态
Promise的状态必须是三个状态中的之一:pending, fulfilled, rejected。
- 当Promise的状态是pending的时候,可以转移到fulfilled或者rejected的状态。
- 当Promise的状态是fulfilled的时候,不允许转移到其它的状态,必须拥有一个不可变的
value
。 - 当Promise的状态是rejected的时候,不允许转移到其它的状态,必须拥有一个不可变的
reason
。
不可变指的是对象不能变化,但是并没有限制对象内的属性不能被修改。
then方法
Promise必须实现then
方法来访问它当前的value
或者最终的value
或者最终的reason
。
then
方法接收两个参数:
promise.then(onFulfilled, onRejected);
onFulfilled
和onRejected
是可选参数,如果它们的类型不是函数,就必须忽略掉。- 如果
onFulfilled
是一个函数- 必须等到Promise的状态切换到fulfilled之后才可以被调用,调用的时候传递的第一个参数是Promise的
value
- 在Promise的状态切换到fulfilled之前不允许被调用
- 只允许被调用一次
- 必须等到Promise的状态切换到fulfilled之后才可以被调用,调用的时候传递的第一个参数是Promise的
- 如果
onRejected
是一个函数- 必须等到Promise的状态切换到rejected之后才可以被调用,调用的时候传递的第一个参数是Promise的
reason
- 在Promise的状态切换到rejected之前不允许被调用
- 只允许被调用一次
- 必须等到Promise的状态切换到rejected之后才可以被调用,调用的时候传递的第一个参数是Promise的
onFulfilled
和onRejected
在执行上下文堆栈只包含平台代码之前是不允许被调用的。例如:TODOonFulfilled
和onRejected
在调用的时候必须以函数的方式来调用(也就是说没有this
的值)- Promise的
then
方法可以被调用多次- 如果Promise的状态是fulfilled了,那么注册过的
onFulfilled
回调函数的执行顺序应该跟调用then
方法的顺序保持一致。 - 如果Promise的状态是rejected了,那么注册过的
onRejected
回调函数的执行顺序应该跟调用then
方法的顺序保持一致。
- 如果Promise的状态是fulfilled了,那么注册过的
then
方法调用之后必须返回一个Promise:promise2 = promise1.then(onFulfilled, onRejected);
- 如果
onFulfilled
或者onRejected
返回一个值x
,那么执行Promise Resolution Procedure,简写为:[[Resolve]](promise2, x)
- 如果
onFulfilled
或者onRejected
抛出了一个异常e
,promise2
的状态必须切换到rejected,并且对应的reason
是e
。 - 如果
onFulfilled
不是函数,但是promise1
的状态切换到了fulfilled,promise2
的状态也必须切换到fulfilled,并且它的value
跟promise1
是同一个value
,也就是promise2.value === promise1.value
。 - 如果
onRejected
不是函数,但是promise1
的状态切换到了rejected,promise2
的状态也必须切换到rejected,并且它的reason
跟promise1
是同一个reason
,也就是promise2.reason === promise1.reason
。
- 如果
Promise Resolution Procedure
Promise Resolution Procedure是一个抽象的操作过程,它的输入是promise和value,我们一般简写为:[[Resolve]](promise, x)
。
如果x
是thenable
,那么x
在尝试让promise
适应x
的状态转换,因为thenable
可以让x
看起来像一个Promise;否则的话,那么promise
的状态切换到fulfilled,并且对应的value
是x
。
thenable
的存在可以让不同的Promise实现之间可以相互操作,只要这些实现暴露一个跟 Promises/A+ 规范兼容的then
方法即可。另外,它也允许 Promises/A+ 的实现通过合理的then
方法来『同化』非兼容的实现方案。
执行[[Resolve]](promise, x)
的过程如下:
- 如果
promise
和x
是同样的对象,也就是promise === x
,promise
的状态切换到rejected,对应的reason
是TypeError - 如果
x
是一个Promise- 当
x
的状态是pending的时候,promise
的状态必须保持pending,直到x
的状态切换到fulfilled或者rejected - 当
x
的状态是fulfilled,promise
的状态必须切换到fulfilled,对应的value
跟x
的value
是同一个值 - 当
x
的状态是rejected,promise
的状态必须切换到rejected,对应的reason
跟x
的reason
是同一个值
- 当
- 如果
x
是一个对象或者函数var then = x.then
- 如果执行
x.then
的时候抛出了异常e
,那么promise
的状态切换到rejected,对应的reason
是e
- 如果
then
是一个函数,调用then.call(x, resolvePromise, rejectPromise)
- 当
resolvePromise
被调用的时候,如果传递的value
是y
,那么执行[[Resolve]](promise, y)
- 当
rejectPromise
被调用的时候,如果传递的reason
是r
,那么promise
的状态切换到rejected,对应的reason
是r
- 如果
resolvePromise
和rejectPromise
都被调用了,或者resolvePromise
,rejectPromise
被调用了多次,那么只有第一次调用有效,后续的调用应该被忽略掉。 - 如果调用
then
的时候抛出了异常e
- 如果
resolvePromise
或者rejectPromise
已经被调用了,那么忽略这个异常 - 否则
promise
的状态切换到rejected,对应的reason
是e
- 如果
- 当
- 如果
then
不是函数,promise
的状态切换到fulfilled,对应的value
是x
- 如果
x
既不是对象也不是函数,promise
的状态切换到fulfilled,对应的value
是x
如果执行[[Resolve]](promise, thenable)
的时候导致又执行了[[Resolve]](promise, thenable)
,那么按照上述提到的算法会导致无限递归。实现Promise的时候是可以作一些必要的检测工作的,但是并不是必须要做的。如果检测到无限递归的情况出现,promise
的状态必须切换到rejected,对应的reason
是TypeError。
注意事项
- Here “platfform 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.