时期 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 均不相同,那么递归下去永远是正确的行为。
