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
对象。
参考资料
写作:
进阶(之后阅读):