Promise/A+规范-翻译

Promise 表示一个异步操作的最终结果,与之进行交互的方式主要是 then 方法,该方法注册了两个回调函数,用于接收 promise 的终值或本 promise 不能执行的原因。

本规范详细列出了 then 方法的执行过程,所有遵循 Promises/A+ 规范实现的 promise 均可以本标准作为参照基础来实施 then 方法。因而本规范是十分稳定的。尽管 Promise/A+ 组织有时可能会修订本规范,但主要是为了处理一些特殊的边界情况,且这些改动都是微小且向下兼容的。如果我们要进行大规模不兼容的更新,我们一定会在事先进行谨慎地考虑、详尽的探讨和严格的测试。
从历史上说,本规范实际上是把之前 Promise/A 规范 中的建议明确成为了行为标准:我们一方面扩展了原有规范约定俗成的行为,一方面删减了原规范的一些特例情况和有问题的部分。

最后,核心的 Promises/A+ 规范不设计如何创建、解决和拒绝 promise,而是专注于提供一个通用的 then 方法。上述对于 promises 的操作方法将来在其他规范中可能会提及。

1. 术语

  • 1.1. promise: 一个拥有符合这个规范的行为的then方法的对象或函数。

  • 1.2. thenable: 定义了一个then方法的对象或函数。

  • 1.3. 值(value): 任意合法的JavaScript值(包括undefined,thenable,promise)。

  • 1.4. 异常(exception): 使用throw语句抛出的一个值

  • 1.5. 原因(reason): 表示promise为什么被拒绝的一个值

2. 必要条件

2.1. Promise 状态

promise必须是这三个状态中的一种:等待态pending,解决态fulfilled或拒绝态rejected

2.1.1 当一个promise处于等待状态的时候:
  • 2.1.1.1 可能变为解决或者拒绝状态。
2.1.2. 当一个promise处于解决状态的时候:
  • 2.1.2.1. 一定不能转换为任何其它状态

  • 2.1.2.2. 必须有一个不能改变的值

2.1.3. 当一个promise处于拒绝状态的时候:
  • 2.1.3.1. 一定不能转换为任何其它状态

  • 2.1.3.2. 必须有一个不能改变的值

在这里,"一定不能改变"意味着不变的身份(例如 ===),但是并不意味着深度不可变性。(译注者:这里应该是说只要值的引用相同即可,并不需要引用中的每一个值都相等)

2.2. then 方法

Promise必须提供一个then方法来访问当前或最终的值或原因。

Promise的then方法接受俩个参数

promise.then(onFulfilled, onRejected)
2.2.1 onFulfilled和onRejected都是可选的参数
  • 2.2.1.1. 如果onFulfilled不是一个函数,它必须被忽略
  • 2.2.1.2. 如果onRejected不是一个函数,它必须被忽略
2.2.2. 如果onFulfilled是一个函数
  • 2.2.2.1. 它必须在promise被解决后调用,promise的值作为它的第一个参数。

  • 2.2.2.2. 它一定不能在promise被解决前调用。

  • 2.2.2.3. 它一定不能被调用多次。

2.2.3. 如果onRejected是一个函数
  • 2.2.3.1. 它必须在promise被拒绝之后调用,用promise的原因作为它的第一个参数。

  • 2.2.3.2. 它一定不能在promise被拒绝之前调用。

  • 2.2.3.3. 它一定不能被调用多次

2.2.4. 在执行上下文栈中只包含平台代码之前,onFulfilled或onRejected一定不能被调用 [3.1]
2.2.5. onFulfilled和onRejected一定被作为函数调用(没有this值) [3.2]
2.2.6. 同一个promise上的then可能被调用多次
  • 2.2.6.1. 如果promise被解决,所有相应的onFulfilled回调必须按照他们原始调用then的顺序执行

  • 2.2.6.2. 如果promise被拒绝,所有相应的onRejected回调必须按照他们原始调用then的顺序执行

2.2.7. then必须返回一个promise [3.3]
promise2 = promise1.then(onFulfilled,onRejected)
  • 2.2.7.1. 如果onFulfilled或onRjected返回一个值x,运行promise解决程序[Resolve]

  • 2.2.7.2. 如果onFulfilled或onRejected抛出一个异常e,promise2必须用e作为原因被拒绝

  • 2.2.7.3. 如果onFulfilled不是一个函数并且promise1被解决,promise2必须用与promise1相同的值被解决

  • 2.2.7.4. 如果onRejected不是一个函数并且promise1被拒绝,promise2必须用与promise1相同的原因被拒绝

2.3. Promise解决程序

promise解决程序是一个抽象操作,它以一个promise和一个值作为输入,我们将其表示为[[Resolve]](promise, x)。如果x是一个thenable,它尝试让promise采用x的状态,并假设x的行为至少在某种程度上类似于promise。否则,它将会用值x解决 promise。

这种thenable的特性使得Promise的实现更具有通用性:只要其暴露一个遵循Promise/A+协议的then方法即可。这同时也使遵循Promise/A+规范的实现可以与那些不太规范但可用的实现能良好共存。

要运行[[Resolve]](promise, x),需要执行如下步骤:

2.3.1. 如果promise和x引用同一个对象,用一个TypeError作为原因来拒绝promise
2.3.2. 如果x是一个promise,采用它的状态:[3.4]
  • 2.3.2.1. 如果x是等待态,promise必须保持等待状态,直到x被解决或拒绝

  • 2.3.2.2. 如果x是解决态,用相同的值解决promise

  • 2.3.2.3. 如果x是拒绝态,用相同的原因拒绝promise

2.3.3. 否则,如果x是一个对象或函数
  • 2.3.3.1. 让then成为x.then。[3.5]

  • 2.3.3.2. 如果检索属性x.then导致抛出了一个异常e,用e作为原因拒绝promise

  • 2.3.3.3. 如果then是一个函数,用x作为this调用它。then方法的参数为俩个回调函数,第一个参数叫做resolvePromise,第二个参数叫做rejectPromise:

    • 2.3.3.3.1. 如果resolvePromise用一个值y调用,运行[[Resolve]](promise, y)。译者注:这里再次调用[Resolve],因为y可能还是promise

    • 2.3.3.3.2. 如果rejectPromise用一个原因r调用,用r拒绝promise。译者注:这里如果r为promise的话,依旧会直接reject,拒绝的原因就是promise。并不会等到promise被解决或拒绝

    • 2.3.3.3.3. 如果resolvePromise和rejectPromise都被调用,或者对同一个参数进行多次调用,那么第一次调用优先,以后的调用都会被忽略。译者注:这里主要针对thenable,promise的状态一旦更改就不会再改变。

    • 2.3.3.3.4. 如果调用then抛出了一个异常e,

    • 2.3.3.4.1. 如果resolvePromise或rejectPromise已经被调用,忽略它

    • 2.3.3.4.2. 否则,用e作为原因拒绝promise

  • 2.3.3.4. 如果then不是一个函数,用x解决promise

2.3.4. 如果x不是一个对象或函数,用x解决promise

如果promise用一个循环的thenable链解决,由于[[Resolve]](promise, thenalbe)的递归特性,最终将导致[[Resolve]](promise, thenable)被再次调用,遵循上面的算法将会导致无限递归。规范中并没有强制要求处理这种情况,但也鼓励实现者检测这样的递归是否存在,并且用一个信息丰富的TypeError作为原因拒绝promise。[3.6]

译者注:这里的循环thenable可能是指如下情况:

const obj = {
    then:function() { 
      //...    
    }
}
obj.then.then = obj.then

这样obj对象中的then将会形成一个环,可以一直无限循环调用.then方法。(类似于window全局对象,window.window依旧是window自己)

由于resolvePromise方法中会对返回值(参数x)的类型进行判断,这样会导致返回值的类型一直为promise,即无限循环调用resolvePromise。
resolvePromise递归调用参考

3. 注解

  • 3.1. 这里“平台代码”意味着引擎、环境以及promise的实现代码。在实践中,这需要确保onFulfilled和onRejected异步地执行,并且应该在then方法被调用的那一轮事件循环之后用新的执行栈执行。这可以用如setTimeout或setImmediate这样的“宏任务”机制实现,或者用如MutationObserver或process.nextTick这样的“微任务”机制实现。由于promise的实现被考虑为“平台代码”,因此在自身处理程序被调用时可能已经包含一个任务调度队列。

  • 3.2. 严格模式下,它们中的this将会是undefined;在非严格模式,this将会是全局对象。

  • 3.3. 假如实现满足所有需求,可以允许promise2 === promise1。每一个实现都应该记录是否能够产生promise2 === promise1以及什么情况下会出现promise2 === promise1。

  • 3.4. 通常,只有x来自于当前实现,才知道它是一个真正的promise。这条规则允许那些特例实现采用符合已知要求的Promise的状态。

  • 3.5. 这个程序首先存储x.then的引用,之后测试那个引用,然后再调用那个引用,这样避免了多次访问x.then属性。此类预防措施对于确保访问者属性的一致性非常重要,因为访问者属性的值可能在俩次检索之间发生变化。

  • 3.6. 实现不应该在thenable链的深度上做任意限制,并且假设超过那个任意限制将会无限递归。只有真正的循环才应该引发一个TypeError;如果遇到一个无限循环的thenable,永远执行递归是正确的行为。

摘抄自:https://zhuanlan.zhihu.com/p/143204897(https://zhuanlan.zhihu.com/p/143204897)

posted @ 2021-04-16 14:07  纳齐尔  阅读(223)  评论(0编辑  收藏  举报