写点什么

【Promise 源码学习】第四篇 - 翻译并理解 Promise A+ 规范

作者:Brave
  • 2021 年 11 月 11 日
  • 本文字数:8855 字

    阅读完需:约 29 分钟

一,前言


上一篇,根据对 Promise 的分析和了解,实现了一个简版 Promise,主要涉及以下内容:


  • Promise 的实现思路;

  • Promise A+ 规范(简版);

  • Promise 简版实现和功能测试;


本篇,翻译并理解 Promise A+ 规范;


备注:虽然 Promise 的翻译网上很多,还是决定按自己的理解翻译一遍;


二,1)Promise A+ 简介


官网:https://promisesaplus.com/

原文

An open standard for sound, interoperable JavaScript promises—by implementers, for implementers.
A promise represents the eventual result of an asynchronous operation. The primary way of interacting with a promise is through its then method, which registers callbacks to receive either a promise’s eventual value or the reason why the promise cannot be fulfilled.
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 changes 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.
复制代码

翻译

  • 一个开放、健全且通用的 JavaScript Promise 标准。由开发者制定,供开发者参考。

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

  • 该规范详细说明了 then 方法的行为,所有遵循该 Promises/A+ 规范实现的 promise 都能够以本标准作为参照基础来实现 then 方法。因而本规范是十分稳定的。尽管 Promises/A+ 组织可能偶尔会修改本规范,会进行一些向下兼容的更改以解决新发现的边界情况,但只有在仔细考虑、讨论和测试之后,我们才会进行大规模的或不兼容的更改。

  • 从历史上看,Promises/A+规范使早起的 Promises/A 提案中的行为条款更加清晰,并扩展了原有规范约定俗成的行为,删减了原规范的一些特例情况和有问题的部分。

  • 最后,核心的 Promises/A+规范不涉及如何创建、实现或拒绝 promise,而是专注于提供一个通用的 then 方法。未来在配套规范中的工作中可能涉及这些主题。


三,2)Terminology(术语)

1.1 “promise” is an object or function with a then method whose behavior conforms to this specification.
1.2 “thenable” is an object or function that defines a then method.
1.3 “value” is any legal JavaScript value (including undefined, a thenable, or a promise).
1.4 “exception” is a value that is thrown using the throw statement.
1.5 “reason” is a value that indicates why a promise was rejected.
复制代码

翻译

1.1 Promisepromise 是一个拥有 then 方法的对象或函数,其行为符合本规范;
1.2 thenable是一个定义了(拥有) then 方法的对象或函数;
1.3 值(value)是任何合法的 JavaScript 值(包括 undefined , thenable 和 promise);
1.4 异常(exception)是使用 throw 语句抛出的一个值。
1.5 原因(reason)是一个值,表示 promise 的拒绝原因。
复制代码



四,3)Requirements(要求)

3.1,Promise States(Promise 状态)

A promise must be in one of three states: pending, fulfilled, or rejected.
2.1.1,When pending, a promise: 2.1.1.1,may transition to either the fulfilled or rejected state.2.1.2 When fulfilled, a promise: 2.1.2.1must not transition to any other state. 2.1.2.2must have a value, which must not change.2.1.3 When rejected, a promise: 2.1.3.1 must not transition to any other state. 2.1.3.2 must have a reason, which must not change.Here, “must not change” means immutable identity (i.e. ===), but does not imply deep immutability.
复制代码


  • 一个 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,必须有一个原因,且不可被更改


这里的“不可变”,指的是恒等(即可用 === 判断相等),但并不意味着深层次的不可变(指当前的引用空间不能被更改)

3.2,The then Method(then 方法)

A promise must provide a then method to access its current or eventual value or reason.A promise’s then method accepts two arguments:promise.then(onFulfilled, onRejected)
一个 promise 必须提供一个 then 方法以访问其当前值、最终值和失败原因。promise 的 then 方法接受两个参数: promise.then(onFulfilled, onRejected)
2.2.1,Both onFulfilled and onRejected are optional arguments: 2.2.1.1,If onFulfilled is not a function, it must be ignored. 2.2.1.2,If onRejected is not a function, it must be ignored. 2.2.1,onFulfilled and onRejected 都是可选参数: 2.2.1.1,如果 onFulfilled 不是一个 function, 那么 onFulfilled 函数必须被忽略. 2.2.1.2,如果 onRejected 不是一个 function, 那么 onRejected 函数必须被忽略. 2.2.2,If onFulfilled is a function: 2.2.2.1,it must be called after promise is fulfilled, with promise’s value as its first argument. 2.2.2.2,it must not be called before promise is fulfilled; 2.2.2.3,it must not be called more than once; 2.2.2,如果 onFulfilled 是一个 function: 2.2.2.1,在 promise 状态为成功态时,onFulfilled必须被调用,promise 的值作为其第一个参数; 2.2.2.2,在 promise 状态为成功态前,onFulfilled不可被调用; 2.2.2.3,onFulfilled 不能被调用多次; 2.2.3,如果 onRejected 是一个 function: 2.2.3.1,在 promise 状态为失败态时,onRejected 必须被调用,promise 的原因作为其第一个参数; 2.2.3.2,在 promise 状态为失败态前,onRejected 不可被调用; 2.2.3.3,onRejected 不能被调用多次; 2.2.4,onFulfilled or onRejected must not be called until the execution context stack contains only platform code. [3.1].2.2.4,onFulfilled 或 onRejected 只允许在 execution context 栈仅包含平台代码时运行. [3.1].
2.2.5,onFulfilled and onRejected must be called as functions (i.e. with no this value). [3.2]2.2.5,onFulfilled 和 onRejected 必须被当做函数调用 (ie 中的 this 为undefined). [3.2]
2.2.6,then may be called multiple times on the same promise. 2.2.6.1,If/when promise is fulfilled, all respective onFulfilled callbacks must execute in the order of their originating calls to then. 2.2.6.2,If/when promise is rejected, all respective onRejected callbacks must execute in the order of their originating calls to then. 2.2.6,同一个 promise,then 方法可被多次调用 2.2.6.1,当 promise 为成功态时,所有 onFulfilled 回调函数都必须按照其注册顺序被执行 2.2.6.2,当 promise 为失败态时,所有 onRejected 回调函数都必须按照其注册顺序被执行 2.2.7,then must return a promise [3.3]. promise2 = promise1.then(onFulfilled, onRejected); 2.2.7.1,If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x). 2.2.7.2,If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason. 2.2.7.3,If onFulfilled is not a function and promise1 is fulfilled, promise2 must be fulfilled with the same value as promise1. 2.2.7.4,If onRejected is not a function and promise1 is rejected, promise2 must be rejected with the same reason as promise1. 2.2.7,then方法必须返回一个 promise 对象 [3.3]. promise2 = promise1.then(onFulfilled, onRejected); 2.2.7.1,如果 onFulfilled 或 onRejected 返回值x,则执行 Promise 解析流程[[Resolve]](promise2, x). 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 相同.
复制代码

3.3,The Promise Resolution Procedure(Promise 的解析过程)

The promise resolution procedure is an abstract operation taking as input a promise and a value, which we denote as [[Resolve]](promise, x). If x is a thenable, it attempts to make promise adopt the state of x, under the assumption that x behaves at least somewhat like a promise. Otherwise, it fulfills promise with the value x.Promise的解析过程,是以一个 promise 和 一个值 做为参数的抽象过程,可表示为[[Resolve]](promise, x).如果 x 是一个 thenable,它试图使 promise 采用 x 状态,假设 x 的行为至少有点像 promise .否则,使用 x 的值执行promise.
This treatment of thenables allows promise implementations to interoperate, as long as they expose a Promises/A+-compliant then method. It also allows Promises/A+ implementations to “assimilate” nonconformant implementations with reasonable then methods.
To run [[Resolve]](promise, x), perform the following steps:要运行[[Resolve]](promise,x),请执行以下步骤:
2.3.1,If promise and x refer to the same object, reject promise with a TypeError as the reason.2.3.1,如果promise 和 x 指向相同的值, 使用 TypeError做为原因将promise拒绝。
2.3.2,If x is a promise, adopt its state [3.4]: 2.3.2.1,If x is pending, promise must remain pending until x is fulfilled or rejected. 2.3.2.2,If/when x is fulfilled, fulfill promise with the same value. 2.3.2.3,If/when x is rejected, reject promise with the same reason.2.3.2,如果 x 是一个 promise, 采用它的状态 [3.4]: 2.3.2.1,如果 x 是 pending 状态,promise必须保持pending,直至 x 转变为 fulfilled 或 rejected. 2.3.2.2,如果 x 是 fulfilled 状态,fulfill promise 使用 x 值. 2.3.2.3,如果 x 是 rejected 状态, reject promise 使用 x 值作为 reason.
2.3.3,Otherwise, if x is an object or function, 2.3.3.1,Let then be x.then. [3.5] 2.3.3.2,If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason. 2.3.3.3,If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise, where: 2.3.3.3.1,If/when resolvePromise is called with a value y, run [[Resolve]](promise, y). 2.3.3.3.2,If/when rejectPromise is called with a reason r, reject promise with r. 2.3.3.3.3,If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored. 2.3.3.3.4,If calling then throws an exception e, 2.3.3.3.4.1,If resolvePromise or rejectPromise have been called, ignore it. 2.3.3.3.4.2,Otherwise, reject promise with e as the reason. 2.3.3.4,If then is not a function, fulfill promise with x.2.3.3,如果 x 是一个对象或一个函数,2.3.3.1,将 then 赋值为 x.then. [3.5] 2.3.3.2,如果取 x.then 的值时抛出了异常,使用此异常做为promise reject 的 reason 值. 2.3.3.3,如果 then 是一个函数, 以 x 作为this 调用 这个 then 函数, 第一个参数为 resolvePromise,第二个参数是rejectPromise: 2.3.3.3.1,如果 resolvePromise 以 y 值被调用, 执行 [[Resolve]](promise, y). 2.3.3.3.2,如果 rejectPromise 以 r 为 reason 被调用, 则以 r 为 reason 将 promise 拒绝. 2.3.3.3.3,如果 resolvePromise 和 rejectPromise 都被调用了,或以相同的参数多次被调用,则只第一次调用生效,后面的调用将被忽略. 2.3.3.3.4,如果调用 then 时抛出了异常: 2.3.3.3.4.1,如果 resolvePromise 或 rejectPromise 已经被调用了,则忽略它. 2.3.3.3.4.2,否则, 则 reject promise 以 e 作为 reason. 2.3.4,If x is not an object or function, fulfill promise with x.2.3.4,如果 x 不是一个对象或函数,则 fulfill promise 以 x 为值 。
If a promise is resolved with a thenable that participates in a circular thenable chain, such that the recursive nature of [[Resolve]](promise, thenable) eventually causes [[Resolve]](promise, thenable) to be called again, following the above algorithm will lead to infinite recursion. Implementations are encouraged, but not required, to detect such recursion and reject promise with an informative TypeError as the reason. [3.6]

Promise 解决过程 是一个抽象的操作,其需输入一个 promise 和一个值,我们表示为 [[Resolve]](promise, x),如果 x 有 then 方法且看上去像一个 Promise ,解决程序即尝试使 promise 接受 x 的状态;否则其用 x 的值来执行 promise 。
这种 thenable 的特性使得 Promise 的实现更具有通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法即可;这同时也使遵循 Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。
运行 [[Resolve]](promise, x) 需遵循以下步骤:
2.3.1 x 与 promise 相等如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
2.3.2 x 为 Promise如果 x 为 Promise ,则使 promise 接受 x 的状态:注4
如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝如果 x 处于执行态,用相同的值执行 promise如果 x 处于拒绝态,用相同的据因拒绝 promise2.3.3 x 为对象或函数如果 x 为对象或者函数:
把 x.then 赋值给 then注5如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise如果then是函数,将x作为函数的作用域this调用之。传递两个回调函数作为参数,第一个参数叫做resolvePromise,第二个参数叫做rejectPromise:如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用如果调用then方法抛出了异常e:如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之否则以 e 为据因拒绝 promise如果 then 不是函数,以 x 为参数执行 promise2.3.4 x 不为对象或函数如果 x 不为对象或者函数,以 x 为参数执行 promise
如果一个 promise 被一个循环的 thenable 链中的对象解决,而 [[Resolve]](promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励施者检测这样的递归是否存在,若检测到存在则以一个可识别的 TypeError 为据因来拒绝 promise。注6
复制代码

五,4)Notes(注释)

3.1,Here “platform code” means engine, environment, and promise implementation code. In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack. This can be implemented with either a “macro-task” mechanism such as setTimeout or setImmediate, or with a “micro-task” mechanism such as MutationObserver or process.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.
3.2,That is, in strict mode this will be undefined inside of them; in sloppy mode, it will be the global object.
3.3,Implementations may allow promise2 === promise1, provided the implementation meets all requirements. Each implementation should document whether it can produce promise2 === promise1 and under what conditions.
3.4,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.
3.5,This procedure of first storing a reference to x.then, then testing that reference, and then calling that reference, avoids multiple accesses to the x.then property. Such precautions are important for ensuring consistency in the face of an accessor property, whose value could change between retrievals.
3.6,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.

3.1 注1这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。这个事件队列可以采用“宏任务(macro-task)”机制或者“微任务(micro-task)”机制来实现。由于 promise 的实施代码本身就是平台代码(译者注: 即都是 JavaScript),故代码自身在处理在处理程序时可能已经包含一个任务调度队列或『跳板』。
译者注: 这里提及了 macrotask 和 microtask 两个概念,这表示异步任务的两种分类。在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。
两个类别的具体分类如下:
macro-task: script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering
micro-task: process.nextTick, Promises(这里指浏览器实现的原生 Promise), Object.observe, MutationObserver
详见 stackoverflow 解答 或 这篇博客
3.2 注2也就是说在 严格模式(strict) 中,函数 this 的值为 undefined ;在非严格模式中其为全局对象。
3.3 注3代码实现在满足所有要求的情况下可以允许 promise2 === promise1 。每个实现都要文档说明其是否允许以及在何种条件下允许 promise2 === promise1 。
3.4 注4总体来说,如果 x 符合当前实现,我们才认为它是真正的 promise 。这一规则允许那些特例实现接受符合已知要求的 Promises 状态。
3.5 注5这步我们先是存储了一个指向 x.then 的引用,然后测试并调用该引用,以避免多次访问 x.then 属性。这种预防措施确保了该属性的一致性,因为其值可能在检索调用时被改变。
3.6 注6实现不应该对 thenable 链的深度设限,并假定超出本限制的递归就是无限循环。只有真正的循环递归才应能导致 TypeError 异常;如果一条无限长的链上 thenable 均不相同,那么递归下去永远是正确的行为。
复制代码


To the extent possible under law, the Promises/A+ organization has waived all copyright and related or neighboring rights to Promises/A+ Promise Specification. This work is published from: United States.
在法律允许的范围内,Promises/A+ 组织已放弃 Promises/A+ Promise规范 的所有版权和相关或相邻权利。这项工作发表于:美国。
复制代码



六,结尾


本篇,翻译并理解了整个 Promise A+ 规范;


接下来,将基于 Promise A+ 规范,继续对简版 Promise 代码功能进行完善;


下一篇,Promise 链式调用的实现;

用户头像

Brave

关注

还未添加个人签名 2018.12.13 加入

还未添加个人简介

评论

发布
暂无评论
【Promise 源码学习】第四篇 - 翻译并理解 Promise A+ 规范