提要
为解决传统异步所造成的回调地狱,社区提出了Promise方案并最终将其写入了语言标准。其用法如其名——承诺:即非立即兑现,通过then方法调用其状态,通过catch捕获错误。
状态
Pending:进行中Fulfilled:已成功Rejected:已失败
Promise的另一个特性是无法改变。正如其英文寓意一样,一旦状态改变就无法继续更改。故我们将Fulfilled和Rejected统一称为Resolved(已定型)
缺点
当然Promise也有一些缺点:
- 一旦创建立即执行,无法中途取消。
- 若不设置回调函数则其内部错误不会抛向外部
Pending状态时无法知道目前进展到哪一阶段(开始或即将完成)
用法
1 | var p = new Promise((resolve, reject)=>{ |
如图,我们创建promise时其会立即执行,一旦状态发生改变那么立即调用then方法中的函数。
没看出来?咱们加个定时器试试
1 | function timeout(ms){ |
看到了吧,只要状态一旦改变那么会立即执行then方法。我们平日所写的ajax函数从此也再不需要进行嵌套回调了
1 | var getJSON = (url)=>{ |
看,我们只需要将ajax函数包装并返回promise,就可以得到其状态了。调用更简单,then方法随时接收promise所返回的resolve或reject
1 | getJSON('/admin/getIndentify').then( json => { |
状态传递
如果是promise之间相互调用那么被调用者的状态会决定调用者的状态
1 | var p1 = new Promise((resolve, reject)=>{ |
3s后 p1 状态为reject,p2 在1s之后改变,resolve返回的时 p1。由于 p2 返回的是另一个Promise,导致 p2 的状态无效,所以 p1 的状态决定了 p2 的状态。故后面的then语句都变成了针对 p1 的。最后通过then抛出错误。
Ps:上面的错误是在3s之后抛出,因为得到 p1 状态后 p2 就不再执行,自然那 4s 也就不用等了
then()
then方法的作用是为Promise实例添加状态改变时的回调函数。
Promise.prototype.then()方法返回的是一个新的Promise实例,故可以采用链式写法。
通过链式调用我们就可以完成步骤顺序调用
1 | getJSON('/admin/getUrl').then((data)=>{ |
catch()
catch方法用于指定发生错误时的回调函数,是.then(null, rejection)的简写
reject方法的作用等同于抛出错误。若Promise状态已变为Resolved,再抛出错误是无效的
1 | var p = new Promise( (resolve, reject) => { |
其他不再赘述,我们重点说一下其具体用法:冒泡性质
Promise对象的错误具有冒泡性质,会一直想后传递,直到被捕获为止。即,错误总是会被下一个catch捕获。
1 | getJSON('/admin/getUrl').then( data =>{ |
一般来说不要在then方法中定义Rejected状态的回调参数,利用catch方法可以捕获所有之前所产生的错误
但是注意,一定要记得写catch。否则Promise对象抛出的错误不会传递到外层代码
自定义方法之done()
无论Promise对象的回调链以then还是catch解为,只要最后一个方法抛出错误,都有可能无法捕捉到(Promise内部的错误不会冒泡到全局)。故我们提供一个done方法,它总是处于回调链的尾端,保证抛出任何可能出现的错误
1 | asyncFunc() |
其实现如下
1 | Promise.prototype.done = function(onFulfilled, onRejected){ |
自定义方法之finally()
不论Promise对象最后状态如何都会执行的操作我们放在finally中。比如使用finally关掉服务器
1 | server.listen(8080) |
其实现如下
1 | Promise.prototype.finally = function(callback){ |
Promise.all()
将多个Promise实例包装成一个新的Promise实例,参数不一定为数组,只要有Iterator接口且返回的每个成员都是promise即可。
当数组中所有实例状态都已改变时新实例状态才会跟着改变
1 | var promise = [2,3,5,7,11,13].map(id=>{ |
当上面6个promise的状态都变为fulfilled或其中一个变为rejected才会调用Promise.all()后的函数
Promise.race()
与上面的不同,但凡有一个参数状态改变,实例状态就会跟着改变。那个率先改变的参数的返回值就是传递给实例的参数
Promise.resolve()
通过Promise.resolve()方法可以将现有对象转换为Promise对象
1 | Promise.resolve('foo') |
当然,参数可能是多种形式。对各种形式的参数处理方式不同
参数为Promise实例
不做修改,直接返回
参数为thenable对象
thenable对象是具有then方法的对象
Promise.resolve方法会将此对象转为Promise对象,然后立即执行thenable对象的then方法
参数是原始值或不具then方法
Promise.resolve方法会直接返回一个新的,状态为fulfilled的Promise对象
无参
直接返回一个状态为fulfilled的对象
Promise.reject()
生成一个状态为rejected的Promise对象的实例,