Promise A+ 学习
在js的本身被设计为异步的,那么这就给编码提出了一个要求,该如何去优雅的编写js代码中异步的代码,最早的时候,我们使用回调来处理这个问题。但是同时也产生了回调地狱的问题。
后来则出现了Promise,Promise的出现可以说彻底改变了对于异步代码的编写方式。Promise的影响之深远,可以说es6加入了Promise,koa中yield,Generator也是配合了Promise才实现了类似await,async的功能。知道ES7中真正的async和await也是基于Promise实现的(其实async和await只是Generator的语法糖)。可以说Promise在异步编程中已经无处不在了。
所以了解Promise的规范还是很有必要的。当然最好的是去看一下规范的原文。
Promise A+的英文原文:Promise/A+
下面对于原文的翻译,我将会引用一个网上已有的翻译版本【翻译】Promises/A+规范的内容,在此感激作者。
一个开放、健全且通用的 JavaScript Promise 标准。由开发者制定,供开发者参考。
Promise 表示一个异步操作的最终结果,与之进行交互的方式主要是 then
方法,该方法注册了两个回调函数,用于接收 promise
的终值或本 promise
不能执行的原因。
本规范详细列出了 then
方法的执行过程,所有遵循 Promises/A+ 规范实现的 promise
均可以本标准作为参照基础来实施 then
方法。因而本规范是十分稳定的。尽管 Promise/A+ 组织有时可能会修订本规范,但主要是为了处理一些特殊的边界情况,且这些改动都是微小且向下兼容的。如果我们要进行大规模不兼容的更新,我们一定会在事先进行谨慎地考虑、详尽的探讨和严格的测试。
从历史上说,本规范实际上是把之前 Promise/A 规范 中的建议明确成为了行为标准:我们一方面扩展了原有规范约定俗成的行为,一方面删减了原规范的一些特例情况和有问题的部分。
最后,核心的 Promises/A+ 规范不设计如何创建、解决和拒绝 promise
,而是专注于提供一个通用的 then
方法。上述对于 promises 的操作方法将来在其他规范中可能会提及。
术语
1.1 Promise
promise
是一个拥有 then
方法的对象或函数,其行为符合本规范;
1.2 thenable
是一个定义了 then
方法的对象或函数,文中译作“拥有 then
方法”;
1.3 值(value)
指任何 JavaScript
的合法值(包括 undefined
, thenable
和 promise
);
1.4 异常(exception)
是使用 throw 语句抛出的一个值。
1.5 据因(reason)
表示一个 promise
的拒绝原因。
要求
2.1 Promise 的状态
一个 Promise 的当前状态必须为以下三种状态中的一种:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。
2.1.1 等待态(Pending)
处于等待态时,promise
需满足以下条件:
- 可以迁移至执行态或拒绝态
2.1.2 执行态(Fulfilled)
处于执行态时,promise
需满足以下条件:
- 不能迁移至其他任何状态
- 必须拥有一个不可变的终值
2.1.3 拒绝态(Rejected)
处于拒绝态时,promise
需满足以下条件:
- 不能迁移至其他任何状态
- 必须拥有一个不可变的据因
这里的不可变指的是恒等(即可用 === 判断相等),而不是意味着更深层次的不可变
2.2 then
方法
一个 promise
必须提供一个 then
方法以访问其当前值、终值和据因
promise
的 then
方法接受两个参数:
1 | 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
的终值
类似 onFulfilled(value);
2.2.2.2 在 promise
执行结束前其不可被调用
1 | // 这里的onFulfilled为伪代码,表示onFulfilled的执行函数 |
2.2.2.3 其调用次数不可超过一次
1 | var c = new Promise((onFulfilled, onRejected) => { |
2.2.3 如果 onRejected
是函数:
2.2.3.1 当 promise
被拒绝执行后其必须被调用,其第一个参数为 promise
的据因
2.2.3.2 在 promise
被拒绝执行前其不可被调用
2.2.3.3 其调用次数不可超过一次
2.2.4 onFulfilled
和 onRejected
只有在执行环境堆栈仅包含平台代码时才可被调用
2.2.5 onFulfilled 和 onRejected 必须被作为函数调用(即没有 this 值)
2.2.6 then 方法可以被同一个 promise 调用多次
2.2.6.1 当 promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调
2.2.6.1 当 promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调
2.2.7 then 必须返回一个 promise
1 | promise2 = promise1.then(onFulfilled, onRejected); |
2.2.7.1 如果 onFulfilled 或者 onRejected
返回一个值 x ,则运行下面的 Promise 解决过程:[Resolve]
2.2.7.2 如果 onFulfilled 或者 onRejected
抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e
2.2.7.3 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
2.2.7.4 如果 onRejected
不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因
2.3 Promise 解决过程
注释:这里是描述的是Promise.then() 执行以后返回什么内容,其实Promise.then()一定会返回一个新的Promise,但是具体是什么内容,是和then里的回调函数的返回值有关,下面的x就是then的回调函数的返回值。可以再看一眼上面2.2.7的内容
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 为据因拒绝执行 promise1
2
3
4
5
6
7
8
9
10
11
12var p = new Promise(function(resolve, reject) {
resolve(123);
});
/*
then的回调函数返回了p2, 和promise是同一个变量,那么就会返回一个错误
*/
var p2 = p.then((data) => {
return p2;
});
>> Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
2.3.2 x 为 Promise
如果 x 为 Promise ,则使 promise 接受 x 的状态
2.3.2.1 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝
2.3.2.2 如果 x 处于执行态,用相同的值执行 promise
2.3.2.3 如果 x 处于拒绝态,用相同的据因拒绝 promise
这里其实是我们经常会用到的一个特性,就是promise链式调用,这里就不多解释了
1 | getUserInfo().then((userInfo) => { |
2.3.3 x 为对象或函数
2.3.3.1 把 x.then 赋值给 then
这里相当于创建了一个新的变量then,类似
var then = x.then;
, 作用其实和我们平常的这样做的方式差不多,方便后面的使用。
2.3.3.2 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
1 | function objFactory() { |
2.3.3.3 如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:
1 | // 类似这个代码 |
2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [Resolve]
1 | function objFactory() { |
2.3.3.3.2 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
1 | // 类似 2.3.3.3.1 |
2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
这个就不给例子了,表述也很直接了
2.3.3.3.4 如果调用 then 方法抛出了异常 e:
2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
1 | function objFactory() { |
2.3.3.3.4.2 否则以 e 为据因拒绝 promise
1 | function objFactory() { |
2.3.3.3.5 如果 then 不是函数,以 x 为参数执行 promise
1 | function objFactory() { |
2.3.3.4 如果 x 不为对象或者函数,以 x 为参数执行 promise
1 | var a = new Promise((resolve) => { |
未完待续