时期 Promise 的机制在 jQuery 和 Dojo 中以 Deferred 形式出现。
ECMAScript 6 增加了对 Promises/A+规范的完善支持,即 Promise 类型。

术语

  1. promise 是一个带有 then 方法的对象或函数,且其行符合 Promise/A+ 规范
  2. thenable 是一个定义 then 方法的对象或函数
  3. value 是任意合法的 JavaScript 的值 (包括 undefined,一个 thenable 或者一个 promise 对象)
  4. exception 是使用 throw 语句抛出的一个值
  5. reason 是表示一个 promise 为何被拒绝的值

    要求

    Promise 状态

    一个 Promise 的状态必需为:pending, fulfilled, rejected 三者之一。

  6. pending 状态

    1. 可转为 fulfilled 或 rejected 的状态
  7. fulfilled 状态
    1. 不能转为其它状态
    2. 必需有一个 不可改变 的 value
  8. rejected 状态
    1. 不能转为其它状态
    2. 必需有一个 不可改变 的 reason

      这里 不可改变 指不能改变其身份(也就是 ===),并不代表深层不能改变(如:当 value 或 resson 是引用值时,只要求引用地址不变,但属性值是能被改变)。

then 方法

  1. 一个 promise 对象必需提供一个 then 方法以访问它 当前 value 或 最终的 value 和 reason
  2. then 方法会接收两个参数

    1. promise.then(onFulfilled, onRejected)
    1. onFulfilledonRejected 都是可选参数
      1. onFulfilled 不是一个函数,会被忽略
      2. onRejected 不是一个函数,会被忽略
    2. onFulfilled 是一个函数
      1. 必须在 promise 是 fulfilled 状态后调用,函数的第一个参数是 promise 的 value
      2. promise 在 fulfilled 状态之前不能调用
      3. 不能多过一次调用
    3. onRejected 是一个函数
      1. 必须在 promise 是 rejected 状态后调用,函数的第一个参数是 promise 的 reason
      2. promise 在 rejected 状态之前不能调用
      3. 不能多过一次调用
    4. onFulfilledonRejected 只能在执行上下文堆栈仅包含平台代码才能调用

      平台代码 是代指引擎、环境和 promise 的实现代码。在实践中,这要求确保了 onFulfilledonRejected 异步执行,且在 then 方法调用的那一轮事件轮询之后的新执行中执行。这实现可以通过 setTimeout、setImmediate “宏任务”机制,或是 MutationObserver、process.nextTick “微任务”机制 。由于 promise 都是 JavaScript 实现,故代码自身在处理程序时可能已经包含一个任务调度队列或调用 处理程序的“跳板”。

    5. onFulfilledonRejected 必需被作为函数调用(即没有 this

      也就是说,在严格模式 this 是 undefined;而在非严格模式,是指向全局对象 。

    6. then 方法可以在同一个 promise 多次调用

      1. 当 promise 是 fulfilled 时,所有 onFulfilled 需按照其 then方法注册的顺序依次回调
      2. 当 promise 是 rejected 时,所有 onRjected 需按照其 then方法注册的顺序依次回调
    7. then 必需返回一个 promise 对象

      1. promise2 = promise1.then(onFulfilled, onRejected);
      1. 假如 onFulfilledonRejected 返回一个 value x,则运行 Promise 解决过程 是 [[Resolve]](promise2, x)
      2. 假如 onFulfilledonRejected 抛出一个 execption e,则 promise2 必须为 rejected 状态且 reason 是 e
      3. 假如 onFulfilled 不是一个函数且 promise1 状态为 fulfilled,则 promise2 必须为 fulfilled 状态且其 value 是 prormise1 的 value
      4. 假如 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) 需遵循以下步骤:

  1. 如果 promise 和 x 指向同一对象,以 TypeError 为 reason,reject promise
  2. 如果 x 是一个 promise,接收其状态 (一般来说,如果 x 符合当前实现,我们才认为是真正的 promise。这一规则允许那些特例实现接受合已知要求的 Promises 状态)
    1. 如果 x 处于 pending 状态,promise 需保持 pending 状态直至 x 被 fulfilled 或 rejected
    2. 如果 x 处于 fulfilled 状态,用相同的 value fulfill promise
    3. 如果 x 处于 rejected 状态,用相同的 reason reject promise
  3. 如果 x 是对象或函数
    1. 把 x.then 赋值给 then(这步我们先是存储了一个指向 x.then 的引用,然后测试并调用该引用,以避免多次访问 x.then 属性。这种预防措施确保了该属性的一致性,因为其值可能在检索调用时被改变。)
    2. 如果取 x.then 的值时抛出错误 e ,则以 e 为 reason reject promise
    3. 假如 then 为函数,将 x 作为函数的 this 并调用。第一个参数 resolvePromise,第二个参数 rejectPromise
      1. 如果 resolvePromise 以 value 为 y 调用,运行 [[Resolve](promise, y)]
      2. 如果 rejectPromise 以 reason 为 r 调用,用 r 为 reason reject promise
      3. 如果 resolvePromise 和 rejectPromise 均被调用,或被同一参数调用多次,则优先采用首次调用并忽略剩下调用
      4. 如果调用 then 方法抛出 exception e 时,
        1. 如果 resolvePromiserejectPromise 已经被调用,则忽略
        2. 否则会以 e 为 reason reject promise
    4. 如果 then 不是函数, 以 x 为 value fulfill promise
  4. 如果 x 不是对象或函数,以 x 为 value fulfill promise

如果一个 promise 被一个循环的 thenable 链中的对象解决,而 [[Resolve]](promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励施者检测这样的递归是否存在,若检测到存在则以一个可识别的 TypeError 为据因来拒绝 promise 。

实现不应该对 thenable 链的深度设限,并假定超出本限制的递归就是无限循环。只有真正的循环递归才应能导致 TypeError 异常;如果一条无限长的链上 thenable 均不相同,那么递归下去永远是正确的行为。