Promise then方法的细节(Promises/A+规范完整翻译)

查看了一些Promise相关的教程,发现基本上都只介绍了then、catch、Promise.resolve、Promise.reject、Promise.all、Promise.race等方法的使用,而很少详细介绍链式调用then方法时的细节。而 ES6 Promise 的规范又超级长,看起来难免会费些时间。

偶然发现了 Promises/A+ 规范,这个规范较为简短,并主要介绍了then方法的规范。而 ES6 Promise 也是基于 Promises/A+ 规范而来,因此通过了解 Promises/A+ 规范便可以很深入地了解到在链式调用then方法时的一些细节。

故对 Promises/A+ 规范进行了全篇翻译,其原文地址为https://promisesaplus.com/,本人的翻译如下:


Promises/A+

为实现者提供一个健全的、可互操作的 JavaScript promise 的开放标准。

一个 promise 代表了一个异步操作的最终结果。与一个 promise 交互的主要方式是通过它的 then 方法向 promise 注册回调函数(callbacks),回调函数则用来接收 promise 的的最终值(eventual value)或者promise失败(cannot be fulfilled)的原因。

本规范详细描述了 then 方法的行为,提供了一个可互操作(interoperable)的基础,所有遵循 Promises/A+ 规范的promise实现都可以依赖这个基础。因此,可以认为本规范是非常稳定的。尽管 Promises/A+ 组织可能会偶尔对本规范进行小幅的且向后兼容的修订以处理一些新发现的极端情况,我们只会在谨慎考虑、讨论和测试后才会向规范中加入较大的或向后不兼容的改动。

从历史上来讲,Promises/A+ 规范阐明了更早出现的 Promises/A 提议,并扩展了它以涵盖 promise 的实际行为,也去掉了一些不足之处或有问题的地方。

最终,核心 Promises/A+ 规范并不涉及如何创建、fulfill 或者 reject promises,而是选择专注于提供一个可互操作的 then 方法。在将来的一些伴随规范中可能会涉及如何创建、fulfill 或者 reject promises。

1. 术语


1.1. “promise”指一个拥有 then 方法的对象或函数,且该 then 方法的行为符合本规范。

1.2. “thenable”指一个拥有 then 方法的对象或函数。

1.3. “value”指任意合法的JavaScript值(包括 undefined,一个thenable或一个promise)。

1.4. “exception”指用 throw 抛出的异常。

1.5. “reason”指表明promise为何被rejected的一个值。

2. 规范要求


2.1 Promise状态

一个promise必须处于以下三种状态之一:pending(未决议状态)fulfilled(成功状态)rejected(失败状态)

2.1.1. 当处于 pending 状态时,一个promise:

    2.1.1.1. 可以转变为 fulfilled 状态或 rejected 状态。

2.1.2. 当处于 fulfilled 状态时,一个promise:

    2.1.2.1. 不可再转变为任何其他状态。

    2.1.2.2. 必须有一个 value,且值不可变。

2.1.3. 当处于 rejected 状态时,一个promise:

    2.1.3.1. 不可再转变为任何其他状态。

    2.1.3.2. 必须有一个 reason ,且值不可变。

这里的“不可变”对于对象等值来说是指向的对象不可变,而这并不意味着此对象的属性不可变(译者注:例如一个promise的值为{a:1},若将其属性a的值改为2,也是允许的)。

2.2 then方法

一个promise必须提供一个 then 方法用于读取此promise的当前或最终值(value或reason)。

一个promise的 then 方法接受两个参数:

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 之后此函数必须被调用,且 promisevalue 应作为此函数的第一个参数。

    2.2.2.2. 在 promise 变为 fulfilled 之前此函数必须不被调用。

    2.2.2.3. 此函数被调用的次数不可超过一次。

2.2.3. 若 onRejected 是一个函数,则:

    2.2.3.1. 当 promise 变为 rejected 之后此函数必须被调用,且 promisereason 应作为此函数的第一个参数。

    2.2.3.2. 在 promise 变为 rejected 之前此函数必须不被调用。

    2.2.3.3. 此函数被调用的次数不可超过一次。

2.2.4. 只有当 执行上下文栈 中只包含平台代码(platform code)时 onFulfilledonRejected 才可以被调用。[详情见3.1]

2.2.5. onFulfilledonRejected 必须以函数方式被调用,且没有this值(译者注: 以函数方式调用是指onFulfilled(v)这种方式,不可用 new onFulfilled() 方式或 onFulfilled.call() / onFulfilled.apply() 方式)。[详情见3.2]

2.2.6. 在同一个promise上可以多次调用 then 方法。

    2.2.6.1. 当 promise 变为或已处于 fulfilled 时,所有 onFulfilled 回调必须按照其通过 then 注册的顺序被执行。

    2.2.6.2. 当 promise 变为或已处于 rejected 时,所有 onRejected 回调必须按照其通过 then 注册的顺序被执行。

2.2.7. then 方法必须返回一个promise。[详情见3.3]

promise2 = promise1.then(onFulfilled, onRejected);

    2.2.7.1. 如果 onFulfilledonRejected 返回了一个值 x, 则执行Promise Resolution Procedure(Promise决议程序) [[Resolve]](promise2, x)

    2.2.7.2. 如果 onFulfilledonRejected 抛出了一个异常 e,则 promise2 必须被rejected,且以 e 作为reason。

    2.2.7.3. 如果 onRejected 不是函数且 promise1 已处于 rejected 状态,则 promise2 必须以 promise1reasonrejected

2.3 Promise Resolution Procedure(Promise决议程序)

Promise Resolution Procedure 是以一个promise和一个value为输入的抽象操作,我们将其表示为[[Resolve]](promise, x)。如果 x 是一个 thenable 且行为有几分像promise时,Promise Resolution Procedure 会试图让 promise 变为与 x 相同的状态。否则,promise 会被决议程序以 x 为值 fulfilled

这种处理 thenable 的方式可以使不同的promise实现之间可以互操作,只要这些实现对外暴露了遵循 Promises/A+规范then 方法即可。这种方式也使得遵循 Promises/A+规范 的实现可以“同化”那些没有遵循 Promises/A+规范 但具备合理的 then 方法的promise实现。

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

2.3.1. 如果 promisex 指向同一个对象,则 reject promise 并且以 TypeError 作为 reason

2.3.2. 如果 x 是一个promise,则采用 x 的状态 [详情见3.4]:

    2.3.2.1. 如果 x 处于 pending 状态,则 promise 必须保持 pending 状态直到 xfulfilledrejected

    2.3.2.2. 当 x 变为或已处于 fulfilled 时,用相同的 valuefulfill promise

    2.3.2.3. 当 x 变为或已处于 rejected 时,用相同的 reasonreject promise

2.3.3. 如果 x 是一个对象或函数,

    2.3.3.1. 令 then 指向 x.then。[详情见3.5]

    2.3.3.2. 如果访问 x.then 属性导致抛出异常 e,则以 ereasonreject promise

    2.3.3.3. 如果 then 是一个函数,则调用它,并且以 x 作为 this,以 resolvePromise 作为第一个参数,以 rejectPromise 作为第二个参数,且:

        2.3.3.3.1. 如果或当 resolvePromise 以一个值 y 被调用了,则执行 [[Resolve]](promise, y)

        2.3.3.3.2. 如果或当 rejectPromise 以一个值 r 被调用了,则以 rreject promise

        2.3.3.3.3. 如果 resolvePromiserejectPromise 都被调用了,或者某个已被被多次调用了,则首次发生的调用生效,其余所有调用都被忽略。

        2.3.3.3.4. 如果调用 then 抛出异常 e

            2.3.3.3.4.1 如果 resolvePromiserejectPromise 已经被调用过了,则忽略这个异常。

            2.3.3.3.4.2 否则,以 ereject promise

    2.3.3.4. 如果 then 不是函数,则以 xfulfill promise

2.3.4. 如果 x 既不是对象也不是函数,则以 xfulfill promise

如果一个promise被一个 thenable 决议,且此 thenable 在一个循环的thenable链中,[[Resolve]](promise, thenable) 的递归特性会导致 [[Resolve]](promise, thenable) 再次被调用,根据上面的算法,这会导致无限递归。因此我们鼓励(但不强制要求)promise实现时能检测无限递归,并在出现无限递归时以一个 TypeErrorreject promise

3. 说明


3.1. 这里的“平台代码(platform code)”意为JS引擎、JS执行环境和实现promise的代码。实际上,这条要求是为了确保在 then 被调用的那次事件循环之后,并且是在一个新的调用栈里异步地执行 onFulfilledonRejected。这种方式可以通过 “宏任务(macro-task)” 原理实现,比如 setTimeoutsetImmediate,或通过 “微任务(micro-task)” 原理实现,比如 MutationObserverprocess.nextTick。既然promise的实现被认为是平台代码,那么它本身可能包含一个任务调度队列或者一个用于调用函数的“trampoline”。

3.2. 这里指的是在 严格模式(strict mode) 下,thisonFulfilledonRejected 中值为 undefined;在 非严格模式 下,this 指向全局对象(global object)。

3.3. 即使一个promise实现满足本规范所有要求,这个实现也可能允许出现 promise2 === promise1。每个promise实现应当在文档中说明该实现中是否会出现promise2 === promise1 的情况以及在什么情况下会出现。

3.4. 一般来说,只有当 x 来自当前的实现时,才能知道 x 是一个真正的promise。对于一个已知其符合当前实现的promise,这个条款允许使用特定于实现的方式来 采用(adopt) 该promise的状态。

3.5. 这种首先保存 x.then 的引用,然后测试此引用,再调用此引用的处理,避免了对 x.then 属性的多次访问。这种预防措施在面对一些特殊访问器时十分重要,因为有的访问器每次访问都会导致其值的改变。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值