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)

Promise/A+规范-翻译的更多相关文章

  1. Promise原理讲解 && 实现一个Promise对象 (遵循Promise/A+规范)

    1.什么是Promise? Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一 2.对于几种常见异步编程方案 回调函数 事件监听 发布/ ...

  2. 按照 Promise/A+ 规范逐行注释并实现 Promise

    0. 前言 面试官:「你写个 Promise 吧.」 我:「对不起,打扰了,再见!」 现在前端越来越卷,不会手写 Promise 都不好意思面试了(手动狗头.jpg).虽然没多少人会在业务中用自己实现 ...

  3. Promise A 规范的一个简单的浏览器端实现

    简单的实现了一个promise 的规范,留着接下来模块使用.感觉还有很多能优化的地方,有时间看看源码,或者其他大神的代码 主要是Then 函数.回调有点绕人. !(function(win) { fu ...

  4. 【javascript】Promise/A+ 规范简单实现 异步流程控制思想

    ——基于es6:Promise/A+ 规范简单实现 异步流程控制思想  前言: nodejs强大的异步处理能力使得它在服务器端大放异彩,基于它的应用不断的增加,但是异步随之带来的嵌套.难以理解的代码让 ...

  5. Go语言安全编码规范-翻译(分享转发)

    Go语言安全编码规范-翻译 本文翻译原文由:blood_zer0.Lingfighting完成 如果翻译的有问题:联系我(Lzero2012).匆忙翻译肯定会有很多错误,欢迎大家一起讨论Go语言安全能 ...

  6. [转]MBTiles 1.2 规范翻译

    MBTiles 1.2 可以参考超图的文档MBTiles扩展具体实现可以参考浅谈利用SQLite存储离散瓦片的思路和实现方法 mapbox提供了一个简单实现测试代码,github地址在这里https: ...

  7. 手写一款符合Promise/A+规范的Promise

    手写一款符合Promise/A+规范的Promise 长篇预警!有点长,可以选择性观看.如果对Promise源码不是很清楚,还是推荐从头看,相信你认真从头看到尾,并且去实际操作了,肯定会有收获的.主要 ...

  8. when 让你跳出异步回调噩梦 node.js下promise/A规范的使用

    其实关于promise 的博客,前端时间专门写了一篇关于 promise 规范的文章,promise规范 让 javascript 中的异步调用更加人性化. 简单回忆下: promise/A规范定义的 ...

  9. Promise/A+规范

    1.什么是Promise? Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一 2.对于几种常见异步编程方案 回调函数 事件监听 发布/ ...

随机推荐

  1. vue cli3 创建项目

    1.确认是否由安装由vue 命令提示符 执行 vue -V 如果没有则执行 npm uninstall vue-cli 2.创建项目 vue create demo1 具体操作如下: (1)执行以上命 ...

  2. 【译】Go:程序如何恢复?

    原文:https://medium.com/a-journey-with-go/go-how-does-a-program-recover-fbbbf27cc31e ​ 当程序不能正确处理错误时, 会 ...

  3. RabbitMQ 常用知识点总结

    基础 为什么使用 MQ? 1.削峰:在某个模块接收到超过最大承受的并发量时,可以通过 MQ 排队来使这些削减同一时刻处理的消息量.减小并发量. 2.解耦:在发送 MQ 处理业务时,可以使业务代码与当前 ...

  4. java:编写jar包加密工具,防止反编译

    懒人方案 网盘: 链接:https://pan.baidu.com/s/1x4OB1IF2HZGgtLhd1Kr_AQ提取码:glx7 网盘内是已生成可用工具,下载可以直接使用,使用前看一下READ. ...

  5. 查看python的安装版本,位数及安装路径

    一.想要查看ubuntu中安装的Python路径 方法一:whereis python (用来快速查找任何文件,是一个文件搜索命令,与locate的功能一样.执行whereis python 会将所有 ...

  6. MYSQL数据库数据拆分之分库分表总结 (转)

      数据存储演进思路一:单库单表 单库单表是最常见的数据库设计,例如,有一张用户(user)表放在数据库db中,所有的用户都可以在db库中的user表中查到. 数据存储演进思路二:单库多表 随着用户数 ...

  7. 严重:Exception sending context initialized event to listener instance of class [myJava.MyServletContextListener] java.lang.NullPointerException

    以上错误是我在自定义Servlet监听器时遇到的,首先大致介绍一下我要实现的功能(本人刚开始学,如有错误,请多多指正): 为了统计网站访问量,防止服务器重启后,原访问次数被清零,因此自定义监听器类,实 ...

  8. 去抖动 debounce

    最近才发现 debounce 在 减少DOM操作及资源加载方面得重要性,遂写个博客记录一下 /** * @param {Function} func 要执行的函数 * @param {number} ...

  9. 「POJ3436」ACM Computer Factory题解

    题意: 有很多台机器,可以把物件从一种状态改装成另一种状态,初始全为\(0\),最终状态全为\(1\),让你可以拼凑机器,请问最大总性能为多少,且要求输出方案. 题解: 这道题是真的水啊,我不想写太多 ...

  10. 4.Java基础

    为了项目方便管理,创建空项目 一.注释 平时编写代码,在代码量比较少的时候,还可以看懂自己写的,但是当项目结构一复杂起来,我们就需要用到注释了 注释并不会被执行,是给写代码的人看的 书写注释是一个非常 ...