自从ES6流行起来,Promise 的使用变得更频繁更广泛了,比如异步请求一般返回一个 Promise 对象,Generator 中 yield 后面一般跟 Promise 对象,ES7中 Async 函数中 await 后面一般也是 Promise 对象,还有更多的 NodeAPI 也会返回 Promise 对象,可以说现在的编程中 Promise 的使用无处不在,那么我们是否真的弄懂了 Promise 呢?是否有误用或错误使用 Promise 呢?是否知道 Promise 的实现原理和 Promise 的花样玩法呢?下面让我们一起来探讨一下吧。
Promise 规范
这里只列举规范中的大致内容,详细内容请查看 Promises/A+ 中文 ,这是ES6 Promises的前身,是一个社区规范,它和 ES6 Promises 有很多共通的内容。
状态 Promise 的初始状态是 Pending ,状态只能被转换为(Resolved)Fulfilled或Rejected,状态的转换不可逆。
then 必须有 then 方法,接收两个可选函数参数onFulfilled、onRejected,then方法必须返回一个新的 Promise 对象,为了保证 then 中回调的执行顺序,回调必须使用异步执行。
兼容 不同的 Promise 的实现必须可以互相调用
具体标准的实现将在 中篇 - 手动封装 中详细说明
ES6 Promise API
如果你对 Promise的使用 还不是很了解,可参考阅读以下资料:
(非常推荐大家阅读的资料,对Promise讲解十分细致详尽)
ES6笔记 - Promise模式(我自己阅读《ECMAScript 6 入门》的笔记)
这里只对ES6 Promise API做简要说明
实例方法
.then(resolvedFn, rejectFn) : 为Promise实例添加状态改变时的回调,返回值是一个 新的Promise实例
.catch() : 是 .then(null, rejectFn) 的语法糖,返回值也是一个 新的Promise对象
Promise对象的错误具有冒泡性质,错误会不断的向后传递,直到 .catch() 捕获
正因为 then 和 catch 返回的都是 Promise 对象,所以才可以不断的链式调用
Promise.resolve()
将现有对象转换为Promise对象
如果参数是promise实例,则直接返回这个实例
如果参数是thenabled对象(有then方法的对象),则先将其转换为promise对象,然后立即执行这个对象的then方法
如果参数是个原始值,则返回一个promise对象,状态为resolved,这个原始值会传递给回调
没有参数,直接返回一个resolved的Promise对象
Promise.reject()
同上,不同的是返回的promise对象的状态为rejected
Promise.all()
接收一个Promise实例的数组或具有Iterator接口的对象,
如果元素不是Promise对象,则使用Promise.resolve转成Promise对象
如果全部成功,状态变为resolved,返回值将组成一个数组传给回调
只要有一个失败,状态就变为rejected,返回值将直接传递给回调
all() 的返回值也是新的Promise对象
Promise.race()
同上,区别是,只要有一个Promise实例率先发生变化(无论是状态变成resolved还是rejected)都触发then中的回调,返回值将传递给回调
race()的返回值也是新的Promise对象
Polyfill和扩展类库 Polyfill只需要在浏览器中加载Polyfill类库,就能使用IE10等或者还没有提供对Promise支持的浏览器中使用Promise里规定的方法。
calvinmetcalf/lie 非常简洁的 promise 库,中篇中的手动封装实现就是参考了这个库
jakearchibald/es6-promise 兼容 Promises/A+ 的类库, 它只是 RSVP.js 的一个子集,只实现了Promises 规定的 API。
yahoo/ypromise 这是一个独立版本的 YUI 的 Promise Polyfill,具有和 ES6 Promises 的兼容性
Promise扩展类库
Promise扩展类库除了实现了Promise中定义的规范之外,还增加了自己独自定义的功能。
kriskowal/q 类库 Q 实现了 Promises 和 Deferreds 等规范。 它自2009年开始开发,还提供了面向Node.js的文件IO API Q-IO 等, 是一个在很多场景下都能用得到的类库。
petkaantonov/bluebird这个类库除了兼容 Promise 规范之外,还扩展了取消promise对象的运行,取得promise的运行进度,以及错误处理的扩展检测等非常丰富的功能,此外它在实现上还在性能问题下了很大的功夫。
注意
在项目中,有可能两个不同的模块使用的是两个不同的Promise类库,那么在大部分的Promise的实现中,都是遵循 Promise/A+ 标准和兼容ES6 Promise接口的,也是不同的Promise的实现是可以互相调用的,如何调用,将在下面说明。
loadAsync1().then(function(data1) { loadAsync2(data1).then(function(data2) { loadAsync3(data2).then(okFn, failFn) }); });