Promises/A+规范
博客专区 > 一配 的博客 > 博客详情
Promises/A+规范
一配 发表于2年前
Promises/A+规范
  • 发表于 2年前
  • 阅读 379
  • 收藏 5
  • 点赞 1
  • 评论 2

腾讯云 十分钟定制你的第一个小程序>>>   

摘要: 本文是Promises/A+规范Version 1.1.1 /2014-05-05的中文翻译。

翻译前言:搜索已经有多篇Promises/A+规范的中文翻译,可参见[4],[5]。但[4][5]翻译都难以理解,例如[4]中未保留原规范的章节编号,不容易对比原文。[4]中将reason翻译为"据因",但我理解他想要表达的是“拒因”,有一些文字中用的是“拒因”,但这样翻译很不容易理解。再比如[4]中将fulfilled状态翻译为执行态, 后面中文语句中也很难以理解。[5]中尽可能地将一些专用词汇没有翻译为中文,但错别字较多,行文不畅。本文参考了[4]与[5]的中文翻译,文中强调的must、must not突出翻译了出来,但尽可能保留了原规范的章节编号,术语、状态和一些特定词汇不翻译为中文,直接采用英语反倒使得文字简洁易懂。

下面是Promises/A+规范Version 1.1.1 /2014-05-05的中文翻译。


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

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

本规范详细描述then方法的行为特点,所有遵循 Promises/A+ 规范实现的 promise 均可以本标准作为参照基础来实现then方法。规范应当是十分稳定的。Promise/A+ 组织会不断修订本规范,以解决一些新发现的边边角角问题,但所做的改动会是微小且向后兼容的。如果我们要进行大规模或者向后不兼容的更新,我们一定会在事先进行谨慎地考虑、详尽的探讨和严格的测试。

从历史上说,本规范实际上是澄清了之前 Promise/A 规范 中建议的行为语句,扩展了这些建议中已成为事实上规范的行为,同时删减了原规范中一些不足以成为规范和有问题的部分。

最后,Promises/A+ 规范的核心不涉及如何创建、fulfill或reject promise,而是专注于提供一个可互操作的then方法。上述对于 promises 的操作方法将来在其他规范中可能会提及。

1. 术语

1.1 "promise" 是一个拥有 then 方法的对象或函数,该方法的行为遵循本规范.

1.2“thenable”是一个定义了 then 方法的对象或函数.

1.3“value”指任何 JavaScript 合法值(包括 undefined , thenable对象或promise对象).

1.4“exception”是使用 throw 语句抛出的一个value。

1.5“reason”是表示promise状态为何转换为rejected的一个value.

2. 需求

2.1. Promise状态

    Promise状态必须为pendingfulfilledrejected中的一种。

2.1.1. 当状态为pending时:

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

2.1.2. 当状态为fulfilled时:

       2.1.2.1. 一定不能转换为其他状态。

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

2.1.3. 当状态为rejected时:

   2.1.2.1. 一定不能转换为其他状态。

       2.1.2.2. 必须有一个reason,且必须不可改变。

    上述"必须不可改变“指的是恒等不变(immutable identity:即可用 === 判断相等),而不是意味着深不可变(deep immutability。译者注: 这里需要再研究下)。

2.2. then方法

    promise必须提供一个then方法,用于获取promise当前/最终value或reason。

    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状态为fulfilled时必须被调用,promise的value作为第一个参数。

      2.2.2.2. 当promise状态为fulfilled之前一定不能被调用。

      2.2.2.3. 调用次数一定不能超过一次。

2.2.3.  如果 onRejected 是函数:

      2.2.3.1. 当promise状态为rejected时必须被调用,promise的reason作为第一个参数。

      2.2.3.2. 当promise状态转换为rejected之前一定不能被调用。

      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状态为fulfilled时,必须按调用then方法的顺序依次执行 onFulfilled 回调函数。

      2.2.6.2. 如果/当promise状态为rejected时,必须按调用then方法的顺序依次执行 onRejected 回调函数。 

2.2.7. then方法必须返回一个promise[3.3]

promise2 = promise1.then(onFulfilled, onRejected);

      2.2.7.1. 如果 onFulfilled 或 onRejected 函数返回值为x,那么执行Promise resolution过程 [[Resolve]](promise2, x) 。

      2.2.7.2. 如果 onFulfilled 或 onRejected 函数抛出异常e,那么promise2状态为rejected, e 作为reason。

      2.2.7.3. 如果 onFulfilled 不是函数且promise1状态是fulfilled,那么promise2状态必须是fulfilled且与promise1的value相同。

      2.2.7.4. 如果 onRejected不是函数且promise1状态是rejected,那么promise2状态必须是rejected且与promise1的reason相同。

2.3. Promise resolution过程

     Promise resolution过程是一个抽象操作,需要输入一个promise和一个value,用 [[Resolve]](promise, x) 来表示。如果x是thenable对象,则尝试使promise接受x的状态,这里假定x的行为特性至少类似于一个promise;否则用value x来fulfill promise。

       这样处理then对象可以使promise的实现有更好的互操作: 只要它们暴露出一个遵循Promises/A+规范的then方法。这样处理也使得遵循Promise/A+ 规范的实现可以与那些未完整遵循规范但then方法合理的实现能良好共存。

     [[Resolve]](promise, x) 按照下面步骤来运行:

2.3.1. 如果promise和x指向同一个对象,则reject promise并且用一个TypeError作为reason。

2.3.2. 如果x是一个promise实例,则以x的状态作为promise的状态[3.4]

  2.3.2.1. 如果x的状态为pending,那么promise的状态必须保持为pending,直到x的状态变为fulfilled或者rejected。

      2.3.2.2. 如果/当x的状态为fulfilled,则用同样的value来fulfill promise。

      2.3.2.3. 如果/当x的状态为rejected,则用同样的reason来reject promise

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

      2.3.3.1. 将x.then赋值给then[3.5]

      2.3.3.2. 如果在获取x.then属性时抛出一个异常e,则用e作为reason来reject promise

      2.3.3.3. 如果then是函数类型,则以x作为then函数内部的this指针,以resolvePromise为第一个参数,rejectPromise为第二个参数,调用then函数。这里:

        2.3.3.3.1. 如果/当resolvePromise被调用且value为y,则执行[[Resolve]](promise, y) 。

            2.3.3.3.2. 如果/当rejectPromise被调用且reason为r,则用r来reject promise。

            2.3.3.3.3. 如果 resolvePromise 和 rejectPromise都被调用,或者多次调用的参数都相同,则优先采用首次调用并忽略剩下的调用。

            2.3.3.3.4. 如果调用then方法抛出一个异常e,

      2.3.3.4.1. 如果resolvePromise或rejectPromise已经被调用,则忽略该异常。

      2.3.3.4.2. 否则用e作为reason来reject promise。

       2.3.3.4. 如果then不是函数类型,用x来fulfill promise

2.3.4.如果x不是对象或函数,用x来fulfill promise

    如果一个promise是被一个循环的 thenable 链中的thenable对象resolve,而 [[Resolve]](promise, thenable) 的递归性质又使得 [[Resolve]](promise, thenable) 被再次调用,根据上述算法将会陷入无限递归之中。本规范不强制要求,但鼓励实现者检测这样的递归是否存在,若检测到存在则以一个TypeError为reason来reject promise[3.6]。

3. 备注

3.1. 这里的“平台代码”是指引擎、执行环境和promise实现代码。实践中,这个需求是确保 onFulfilled 和 onRejected 函数为异步执行,且应该在 then 方法被调用的那一轮事件循环之后,在一个新的堆栈中执行。这个事件队列可以采用“宏任务(macro-task)”机制(诸如通过 setTimeout 或 setImmediate )或者“微任务(micro-task)”机制(诸如通过 MutationObserver 或 process.nextTick )来实现。由于 promise 的实现代码被视为平台代码,因此实现代码自身可以包含一个任务调度队列或者handler被调用的跳板


译者注: 这里提及了 macro-task 和 micro-task 两个概念,可参见[6], [7]。当挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,事件循环的每一轮都要处理来自 macro-task 队列(这个队列在 WHATWG specification中被称为任务队列)的一个任务,该任务执行完毕后取出 micro-task 队列中的所有任务顺序执行;之后再是下一轮取出一个macro-task 任务,取出所有的micro-tasks,从而周而复始。

但这样可能会导致当micro-task 队列中所有任务运行完成,执行下一个macro-task 任务时已经过去了很长时间,这会导致UI被阻塞等各种异常。Node.js中process.nextTick函数会在micro-tasks中排队, 有一个内在保护机制process.maxTickDepth可以避免长时间阻塞,这个值缺省为1000, 当这个时间到后就不再处理micro-tasks,而是开始处理下一个macro-task

通常, 当需要以同步方式来做异步访问时使用micro-tasks(即立即执行这一micro任务),否则使用macro-tasks.

举例:

macro-tasks: setTimeout, setInterval, setImmediate, I/O, UI rendering
micro-tasks: process.nextTick, Promises, Object.observe, MutationObserver


3.2. 在strict模式下this指针为undefined,而在sloppy模式下this指针为全局对象。

3.3. 代码实现在满足所有需求点的情况下可以允许 promise2 === promise1 。每个实现都要有文档说明其是否允许以及在何种条件下允许 promise2 === promise1。

3.4. 一般来说,如果按照当前实现,只知道x是个真正的 promise 。这一规则允许那些特例实现 接受 遵循规范要求的Promises的状态。

3.5.  这步我们先是存储了一个指向 x.then 的引用,接下来测试该引用,然后调用该引用,以避免多次访问 x.then 属性。这种预防措施确保了访问该属性的一致性,因为其值可能在检索调用时被改变。

3.6. 实现不应当对thenale链的深度有任何限制, 不应当假定超过该限制就会无限递归。只有真正的循环递归才应能导致 TypeError 异常;如果一条无限长的链上有不同的thenable,那么不断递归就是正确的行为。

参考资料:

[1] Promises/A+, https://promisesaplus.com/

[2] Differences from Promises/A, https://promisesaplus.com/differences-from-promises-a

[3] Conformant Implementations, https://promisesaplus.com/implementations

[4] Promise A+ 规范 中文翻译, http://malcolmyu.github.io/malnote/2015/06/12/Promises-A-Plus/

[5] 前端翻译:Promises/A+规范, 肥仔John, http://www.cnblogs.com/fsjohnhuang/p/4139172.html

[6] Difference between microtask and macrotask within an event loop context, http://stackoverflow.com/questions/25915634/difference-between-microtask-and-macrotask-within-an-event-loop-context

[7] Promise进阶介绍+原生实现, http://wengeezhang.com/?p=11

共有 人打赏支持
粉丝 35
博文 130
码字总数 93733
评论 (2)
炳鑫
感谢一个,then返回什么东东始终确定不了。还是直接看规范好一些。再次感谢
南漂一卒

引用来自“炳鑫”的评论

感谢一个,then返回什么东东始终确定不了。还是直接看规范好一些。再次感谢
promise.then() 返回一个新 Promise 对象,代表前者回调执行的结果~
×
一配
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: