时期 Promise 的机制在 jQuery 和 Dojo 中以 Deferred 形式出现。
ECMAScript 6 增加了对 Promises/A+规范的完善支持,即 Promise 类型。
术语
- promise 是一个带有
then
方法的对象或函数,且其行符合 Promise/A+ 规范 - thenable 是一个定义
then
方法的对象或函数 - value 是任意合法的 JavaScript 的值 (包括 undefined,一个 thenable 或者一个 promise 对象)
- exception 是使用
throw
语句抛出的一个值 -
要求
Promise 状态
一个 Promise 的状态必需为:pending, fulfilled, rejected 三者之一。
pending 状态
- 可转为 fulfilled 或 rejected 的状态
- fulfilled 状态
- 不能转为其它状态
- 必需有一个 不可改变 的 value
- rejected 状态
- 不能转为其它状态
- 必需有一个 不可改变 的 reason
这里 不可改变 指不能改变其身份(也就是
===
),并不代表深层不能改变(如:当 value 或 resson 是引用值时,只要求引用地址不变,但属性值是能被改变)。
then 方法
- 一个 promise 对象必需提供一个
then
方法以访问它 当前 value 或 最终的 value 和 reason then
方法会接收两个参数promise.then(onFulfilled, onRejected)
onFulfilled
和onRejected
都是可选参数onFulfilled
不是一个函数,会被忽略onRejected
不是一个函数,会被忽略
onFulfilled
是一个函数- 必须在 promise 是 fulfilled 状态后调用,函数的第一个参数是 promise 的 value
- promise 在 fulfilled 状态之前不能调用
- 不能多过一次调用
onRejected
是一个函数- 必须在 promise 是 rejected 状态后调用,函数的第一个参数是 promise 的 reason
- promise 在 rejected 状态之前不能调用
- 不能多过一次调用
onFulfilled
或onRejected
只能在执行上下文堆栈仅包含平台代码才能调用平台代码 是代指引擎、环境和 promise 的实现代码。在实践中,这要求确保了
onFulfilled
和onRejected
异步执行,且在 then 方法调用的那一轮事件轮询之后的新执行中执行。这实现可以通过 setTimeout、setImmediate “宏任务”机制,或是 MutationObserver、process.nextTick “微任务”机制 。由于 promise 都是 JavaScript 实现,故代码自身在处理程序时可能已经包含一个任务调度队列或调用 处理程序的“跳板”。onFulfilled
或onRejected
必需被作为函数调用(即没有this
)也就是说,在严格模式 this 是 undefined;而在非严格模式,是指向全局对象 。
then
方法可以在同一个 promise 多次调用- 当 promise 是 fulfilled 时,所有
onFulfilled
需按照其then
方法注册的顺序依次回调 - 当 promise 是 rejected 时,所有
onRjected
需按照其then
方法注册的顺序依次回调
- 当 promise 是 fulfilled 时,所有
then
必需返回一个 promise 对象promise2 = promise1.then(onFulfilled, onRejected);
- 假如
onFulfilled
或onRejected
返回一个 valuex
,则运行 Promise 解决过程 是[[Resolve]](promise2, x)
- 假如
onFulfilled
或onRejected
抛出一个 execptione
,则 promise2 必须为 rejected 状态且 reason 是 e - 假如
onFulfilled
不是一个函数且 promise1 状态为 fulfilled,则 promise2 必须为 fulfilled 状态且其 value 是 prormise1 的 value - 假如
onRejected
不是一个函数且 promise1 状态为 rejected,则 promise2 必须为 rejected 状态且其 reason 是 promise1 的 reason代码实现在满足所有要求下可允许
promise2 === promise1
,每个实现都 要文档说明其是否允许和在何种条件下允许promise2 === promise1
Promise 解决过程
Promise 解决过程是一个抽象的操作,输入一个 promise 和 一个 value。表示为 [[Resolve]](promise, x)
。假如 x 是一个 thenable
(有 then 方法且看上去像一个 Promise),解决程序会尝试使 promise 接收 x 状态,否则用 x 值来执行 promise
这种 thenable
的特性使得 Promise 的实现更具有通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法即可;这同时也使遵循 Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。
运行 [[Resolve]](promise, x)
需遵循以下步骤:
- 如果 promise 和 x 指向同一对象,以 TypeError 为 reason,reject promise
- 如果 x 是一个 promise,接收其状态 (一般来说,如果 x 符合当前实现,我们才认为是真正的 promise。这一规则允许那些特例实现接受合已知要求的 Promises 状态)
- 如果 x 处于 pending 状态,promise 需保持 pending 状态直至 x 被 fulfilled 或 rejected
- 如果 x 处于 fulfilled 状态,用相同的 value fulfill promise
- 如果 x 处于 rejected 状态,用相同的 reason reject promise
- 如果 x 是对象或函数
- 把 x.then 赋值给 then(这步我们先是存储了一个指向 x.then 的引用,然后测试并调用该引用,以避免多次访问 x.then 属性。这种预防措施确保了该属性的一致性,因为其值可能在检索调用时被改变。)
- 如果取 x.then 的值时抛出错误 e ,则以 e 为 reason reject promise
- 假如 then 为函数,将 x 作为函数的 this 并调用。第一个参数
resolvePromise
,第二个参数rejectPromise
- 如果
resolvePromise
以 value 为 y 调用,运行[[Resolve](promise, y)]
- 如果
rejectPromise
以 reason 为 r 调用,用 r 为 reason reject promise - 如果 resolvePromise 和 rejectPromise 均被调用,或被同一参数调用多次,则优先采用首次调用并忽略剩下调用
- 如果调用 then 方法抛出 exception e 时,
- 如果
resolvePromise
或rejectPromise
已经被调用,则忽略 - 否则会以 e 为 reason reject promise
- 如果
- 如果
- 如果 then 不是函数, 以 x 为 value fulfill promise
- 如果 x 不是对象或函数,以 x 为 value fulfill promise
如果一个 promise 被一个循环的 thenable 链中的对象解决,而 [[Resolve]](promise, thenable)
的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励施者检测这样的递归是否存在,若检测到存在则以一个可识别的 TypeError 为据因来拒绝 promise 。
实现不应该对 thenable 链的深度设限,并假定超出本限制的递归就是无限循环。只有真正的循环递归才应能导致 TypeError 异常;如果一条无限长的链上 thenable 均不相同,那么递归下去永远是正确的行为。