提要
为解决传统异步所造成的回调地狱,社区提出了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
对象的实例,