Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[译]Promises/A+ 标准 #8

Open
bigbigbo opened this issue Feb 14, 2019 · 0 comments
Open

[译]Promises/A+ 标准 #8

bigbigbo opened this issue Feb 14, 2019 · 0 comments
Labels
翻译 自己翻译的一些文章
Milestone

Comments

@bigbigbo
Copy link
Owner

bigbigbo commented Feb 14, 2019

Promises/A+ 标准

_20190214092805

一个可靠的、可交互的 JavaScript Promises 开放标准 ——供实现者参考

一个 promise 表示一个异步操作的最终结果。和 promise 交互的主要方式是通过它的 then 方法,通过在 then 方法中注册回调函数来接收 promise 的最终值或者无法实现的原因(拒因)。

本规范详尽描述了 then 方法的行为,为所有 Promises/A+ 的实现提供了一个可交互的基础。因此,该规范将是非常稳定的。尽管 Promises/A+ 组织 可能会偶尔通过修正(向后兼容的 minor 版本)该规范来解决一些新发现的边界情况。只有经过仔细的考虑、讨论和测试之后,我们才会整合出一个大的或向后不兼容的版本。

Promises/A+ 澄清了早期的 Promises/A 提案的行为条款,扩展了一些内容并省略了未明确说明或有问题的部分。

最后,核心的 Promises/A+ 规范不涉及如何create(创建)fulfill(履行)reject(拒绝) promises,而是选择专注于提供一种可交互的 then 方法。未来在配套规范中的工作可能会涉及这些主题。

1. 名词解释

  • 1.1. "promise" 是一个拥有符合本规范行为描述的 then 方法的 object 或者 function
  • 1.2. "thenable" 是一个定义了 then 方法的 object 或者 function
  • 1.3. "value" 是一个 JavaScript 的合法值(包括 undefinedthenable 或者promise
  • 1.4. "exception" 是一个通过 throw 语句抛出的值。
  • 1.5. "reason" 是一个值,用来表示拒绝 promise 的原因。

译者注:
promisethenable 很像,区别就在于 then 方法是否严格符合本规范的定义,例如:

const obj = {
    then() {
        console.log("I'm not a promise")
    }
}

只能称 obj 是一个 thenable 而不能称其为promise,因为它的 then 方法不符合本规范的行为描述。
reason 在之后都称之为为拒因

2. 要求

2.1 Promise 状态

一个 promise 的状态必须是 pendingfulfilledrejected三种状态中的一种。

  • 2.1.1 当 promise 处于 pending 状态时:

    • 2.1.1.1 promise 可以转变为 fulfilled 或者 rejected 状态。
  • 2.1.2 当 promise 处于 fulfilled 状态时:

    • 2.1.2.1 promise 禁止转变为其他状态。
    • 2.1.2.2 必须拥有一个不可变的值
  • 2.1.3 当 promise 处于 rejected 状态时:

    • 2.1.3.1 promise 禁止转变为其他状态。
    • 2.1.3.2 必须拥有一个不可变的拒因

注意:不可变 意味着恒等(即 ===),但不意味着深度不可变。

译者注:当值或者拒因为为引用类型时,只要其引用地址相同即可。

2.2 then 方法

一个 promise 必须提供一种方法来访问其当前的最终值或者拒因。

一个 promisethen 方法接收两个参数:

promise.then(onFulfilled, onRejected)
  • 2.2.1 onFulfilledonRejected 都是可选参数:

    • 2.2.1.1 如果 onFulfilled 不是一个函数,则必须忽略掉。
    • 2.2.1.2 如果 onRejected 不是一个函数,则必须忽略掉。
  • 2.2.2 如果 onFulfilled 是一个函数:

    • 2.2.2.1 只能在 promise 的状态为 fulfilled 时被调用,并且第一个参数是 promise 的终值
    • 2.2.2.2 不能在 promise 状态为 fulfilled 之前被调用
    • 2.2.2.3 只能被调用一次
  • 2.2.3 如果 onRejected 是一个函数:

    • 2.2.3.1 只能在 promise 的状态为 rejected 时被调用,并且第一个参数是 promise 的拒因
    • 2.2.3.2 不能在 promise 状态为 rejected 之前被调用
    • 2.2.3.3 只能被调用一次
  • 2.2.4 onFulfilledonRejected 只允许执行上下文堆栈包含平台代码时才可以被调用。[3.1]

  • 2.2.5 onFulfilledonRejected 必须被作为函数调用(即没有this值)。[3.2]

  • 2.2.6 then 可以被同一个 promise 调用多次。

    • 2.2.6.1 当 promisefulfilled 状态时,所有接收到的 onFulfilled 回调函数必须按照其调用 then 的顺序执行。
    • 2.2.6.2 当 promiserejected 状态时,所有接收到的 onRejected 回调函数必须按照其调用 then 的顺序执行。
  • 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 必须转化为 rejected 状态并以 e 作为拒因。
    • 2.2.7.3 如果 onFulfilled 不是一个函数并且 promise1fulfilled 状态,则 promise2 也必须是 fulfilled 状态并返回相同的值(promise1 返回的值)。
    • 2.2.7.4 如果 onRejected 不是一个函数并且 promise1rejected 状态,则则 promise2 也必须是 rejected 状态并返回相同的拒因(promise1 返回的拒因)。

2.3 Promise 决策程序

Promise 决策程序(promise resolution procedure) 是一个抽象的操作,将 promise 和值作为输入,用代码表示为 [[Resolve]](promise, x)。如果 x 是一个 thenable,则该程序会尝试让 promise 接受 x 的状态,我们假设 x 的行为至少像一个 promise。 否则 promise 则以 x 为最终值直接执行。

这种对 thenable 的处理使得 promise 的实现更加灵活,只要暴露出符合Promises/A+的规范的 then 方法即可。它还允许 Promises/A+兼容不合规范的实现。

运行[[Resolve]](promise, x),将执行以下几种步骤:

  • 2.3.1 如果 promisex 引用相同的对象,则拒绝执行 promise 并且以 TypeError 为拒因。

  • 2.3.2 如果 x 是一个promise,则接受它的状态[3.4]

    • 2.3.2.1 如果 x 处于 pending 状态,则 promise 则必须保持 pending 状态直到 x 变为 fulfilled 或者 rejected 状态。
    • 2.3.2.2 如果 x 处于 fulfilled 状态,则以相同的值执行 promise
    • 2.3.2.3 如果 x 处于 rejected 状态,则以相同的拒因拒绝执行 promise
  • 2.3.3 否则,如果 x 是一个对象或者函数,

    • 2.3.3.1 使用 x.then 作为 promisethen[3.5]
    • 2.3.3.2 如果获取 x.then 的过程中发生了异常 e,则以 e 作为拒因拒绝执行 promise
    • 2.3.3.3 如果 x.then 是一个函数,则以x作为 this 值调用该函数,第一个参数是 resolvePromise, 第二个参数是 rejectPromise,其中:
      • 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则执行 [[Resolve]](promise, y)
      • 2.3.3.3.2 如果 rejectPromise 以拒因 r 为参数被调用,则以 r 为拒因拒绝执行 promise
      • 2.3.3.3.3 如果 resolvePromiserejectPromise 都被调用,或者以同样的参数调用多次,则采用第一次调用并且忽略掉其他的调用。
      • 2.3.3.3.4 如果调用 then 方法中发生异常 e,
        • 2.3.3.3.4.1 如果 resolvePromise 或者 rejectPromise 被调用,则忽略它。
        • 2.3.3.3.4.2 否则以 e 为拒因拒绝执行 promise
    • 2.3.3.4 如果 x.then 不是一个函数,则以x为最终的值执行 promise
  • 2.3.4 如果 x 不是一个对象或者函数,则以x为最终的值执行 promise

如果一个 promise 在一条 由 thenable 组成的循环链中被 resolved,则会导致[[Resolve]](promise, thenable) 执行 [[Resolve]](promise, thenable),造成无限循环。我们鼓励去检测由此导致的错误,并以 TypeError 拒绝执行 promise,但这不是必须的。[3.6]

附录

3.1

这里的 platofrm code 是指引擎、环境和 promise 实现代码。实际上,这一要求确保 onFulfilledonRejected 是异步执行,并且是在 then 所被调用的那一轮的事件循环之后的新执行栈中执行。这边可以使用 setTimeout 或者 setImmediate 这类的**宏任务(macro-task)实现或者使用 MutationObserverprocess.nextTick 这类的微任务(micro-task)**去实现。因为promise的实现被认定为平台代码,他可能包含自身的任务队列。

3.2

也就是说,在严格模式下,this 值可能会是undefined,在非严格模式下,this 指向全局对象。

3.3

允许实现 promise2 === promise1,前提是必须实现所有要求。并且每个实现都应该记录它是否会产生及在什么条件下会产生 promise2 === promise1

3.4

只有符合当前规范的 x 才能被称为真正的 promise

3.5

首先存储对 x.then 的引用,然后测试该引用,然后调用该引用的过程应该避免对 x.then 属性的多次访问。此类预防措施对于确保访问属性的一致性非常重要,访问属性的值可能会在检索过程中发生变化。

3.6

实现不应该去设置 thenable 链的最大长度,并且去假定调用超过了设置的最大长度就是无限循环的调用。只有真正的递归调用才会抛出 TypeError,如果 thenable 链上的每一个 thenable 对象都不相同,那他就应该无限调用下去。

@bigbigbo bigbigbo added the 翻译 自己翻译的一些文章 label Feb 14, 2019
@bigbigbo bigbigbo added this to the 2019-02 milestone Feb 14, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
翻译 自己翻译的一些文章
Projects
None yet
Development

No branches or pull requests

1 participant