Promise 对象
Promise 是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和更强大。
特点:
- 对象的状态不受外界影响。
Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Fulfilled(已完成,又称 Resolved)和Rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态。 - 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
Promise对象的状态改变只有两种可能:从Pending变为Fulfilled和从Pending变为Rejected。
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。
缺点:
- 无法取消
Promise,一旦新建就会立即执行,无法中途取消。 - 如果不设置回调函数,
Promise内部抛出的错误不会反映到外部。 - 当处于
Pending状态时,无法得知目前进展到哪一个阶段(刚开始还是即将完成)。
基本用法
一个栗子🌰,用Promise对象实现 Ajax 操作:
1 | var getJSON = function(url){ |
resolve和reject函数由 JavaScript 引擎提供,不用自己部署。两者在改变 Promise对象的状态的同时,将异步操作的结果或报出的错误作为参数传递出去。
then方法可以接受两个回调函数作为参数(可以说是处理 result 和 err 的回调)。第一个在Promise对象状态变为Fulfilled时调用,第二个(可省)在Promise对象状态变为Rejected时调用,都接受Promise对象传出的值作为参数。
如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出的错误;resolve函数的参数除了正常的值以外,还可能是另一个Promise实例(决定前一个Promise对象的状态),表示异步操作的结果有可能是一个值,也有可能是另一个异步操作。
注意点
1
then方法会返回一个 Promise 实例:
1 | var outputPromise = promise.then( |
这时,outputPromise变成了受function(fulfilled)或者 function(rejected)控制状态的 Promise 实例了:
- 若
function(fulfilled)或者function(rejected)返回一个值(字符串、数组、对象等),那么outputPromise的状态变为Resolved; - 若
function(fulfilled)或者function(rejected)抛出异常(throw new Error(...)),那么outputPromise的状态变为Rejected; - 若
function(fulfilled)或者function(rejected)返回一个 Promise 实例,那么outputPromise就成为这个新的 Promise 实例。
可以看 promise的传递 的例子(使用了 q)加深理解:
1 | var Q = require('q'); |
2
在创建新的 Promise 时,作为 Promise 参数传入的函数是会被立即执行的(而不是调用 then 时才执行),只是其中执行的代码可以是异步代码。
虽然 Promise 作为参数接收的函数是同步执行的,但是**then方法的回调函数执行是异步**的。
🌰:
1 | var p = new Promise(function(resolve, reject){ |
控制台输出:
1 | "create a promise" |
方法
Promise.prototype.then()
then方法返回一个新的Promise实例。链式写法调用then方法时,前一个回调函数将返回结果作为参数,传入第二个回调函数(前一个回调函数没有用return返回结果时,默认返回 undefined)。
Promise.prototype.catch()
reject方法的作用,等同于抛出错误。如果Promise状态已经变成Fulfilled,在resolve语句后再抛出错误是无效的,因为状态不会再改变了。
一般来说,不要在then方法中定义Reject状态的回调函数,总是使用catch方法。因为catch可以捕获之前所有then方法执行中的错误,也更接近同步的try/catch写法。
catch方法返回的也是一个Promise对象。
Promise.all()
Promise.all方法用于将多个Promise实例(否则调用Promise.resolve方法再处理),包装成一个新的Promise实例。接受一个具有Iterator接口,且返回的每个成员都是Promise实例的参数(一般为数组)。
1 | var p = Promise.all([p1, p2, p3]); |
只有每个成员的状态都为Fulfilled,p 的状态才为Fulfilled,所有返回值组成一个参数传递给 p 的回调函数;否则只要有一个成员被rejected,p 的状态为Rejected,第一个被reject的实例的返回值被传递给 p 的回调函数。
如果作为参数的Promise实例自身定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法。
Promise.race()
1 | var p = Promise.race([p1, p2, p3]); |
p 的状态随第一个改变状态的成员而做相同改变,该成员返回值传递给 p 的回调函数。其他与Promise.all方法一致。
Promise.resolve()
将现有对象转为Promise对象。根据参数分为四种情况:
Promise实例:不作任何修改,直接返回该实例;thenable对象(具有then方法的对象):转为Promise对象,然后在立即调用其then方法的同时状态变为Resolved;- 不符合以上情况的任何参数:返回状态为
Fulfilled的Promise对象,参数传给回调函数; - 不带有任何参数:返回状态为
Fulfilled的Promise对象。
注意:当Promise.resolve()的参数是Promise实例时,resolve会“拆箱”获取这个 Promise 实例的状态和值,但这个过程是异步的。
Promise.reject()
返回一个新的Promise实例,状态为Rejected。方法的参数会作为reject的理由,变成后续方法的参数。
reject不具备“拆箱”能力。
应用
- 加载图片:加载完成时
Promise的状态发生变化。 - Generator 函数与 Promise 的结合:使用
Generator函数管理流程,遇到异步操作的时候,通常返回一个Promise对象。
参考资料
写作:
进阶(之后阅读):