本文介绍了JavaScript中Promise以及async函数的使用。
《送友人》
青山横北郭,白水绕东城。
此地一为别,孤蓬万里征。
浮云游子意,落日故人情。
挥手自兹去,萧萧班马鸣。
—唐,李白
Promise简介
Promise 是异步编程的一种解决方案。ES6原生提供 Promise 对象。Promise 对象代表一个异步操作,有三种状态: Pending(进行中)、 Resolved(已完成,又称Fulfilled)和 Rejected(已失败)。
Promise 有两个特点:
- 对象的状态不受外界影响。 Promise 对象代表一个异步操作,有三种状态: Pending (进行中)、 Resolved (已完成,又称Fulfilled)和 Rejected (已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。 Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected 。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。
Promise 也有一些缺点。首先,无法取消 Promise ,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数, Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
Promise简单使用
使用 new 创建一个 promise 对象,Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是resolve 函数和 reject 函数:
1 | var promise = new Promise(function(resolve, reject) { |
resolve 函数的作用是,将Promise对象的状态从Pending变为Resolved,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去; reject 函数的作用是,将Promise对象的状态从Pending变为Rejected,在异步操作失败时调用,并将异步操作报出的异常,作为参数传递出去。
Promise 实例生成以后,可以用 then 方法分别指定 Resolved 状态和 Reject 状态的回调函数。
1 | promise.then(function(value) { // success |
then 方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为Resolved时调用,第二个回调函数是 Promise 对象的状态变为 Reject 时调用。其中,第二个函数是可选的。这两个函数都接受 Promise 对象传出的值作为参数。当不使用第二个回调函数时可以使用 catch 处理异常:
1 | promise.then(function(value) { // success |
使用 then 和 catch 处理异常的区别:
- 使用
promise.then(onFulfilled, onRejected)
的话在 onFulfilled 中发生异常的话,在 onRejected 中是捕获不到这个异常的。 - 在
promise.then(onFulfilled).catch(onRejected)
的情况下 then 中产生的异常能在 .catch 中捕获。
所以我们应该总是使用 catch 处理异常。
Promise.resolve
静态方法 Promise.resolve(value)
可以认为是 new Promise()
方法的快捷方式。比如 Promise.resolve(42);
可以认为是以下代码的语法糖。
1 | new Promise(function(resolve){ |
Promise.reject
Promise.reject(error)
是和 Promise.resolve(value)
类似的静态方法。比如 Promise.reject(new Error("出错了"))
就是下面代码的语法糖形式。
1 | new Promise(function(resolve,reject){ |
then的链式调用
从代码上乍一看, promise.then(...).catch(...)
像是针对最初的 promise 对象进行了一连串的方法链调用。然而实际上不管是 then 还是 catch 方法调用,都返回了一个新的 promise 对象。因此可以采用链式写法,即 then 方法后面再调用另一个 then 方法。
1 | var promise = Promise.resolve(5); |
上面的代码使用 then 方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。每个方法中 return 的值不仅只局限于字符串或者数值类型,也可以是对象或者 promise 对象等复杂类型。return 的值会由 Promise.resolve(return的返回值);
进行相应的包装处理,因此不管回调函数中会返回一个什么样的值,最终 then 的结果都是返回一个新创建的 promise 对象。
1 | var promise = Promise.resolve(5); |
前面所有 promise 对象包括一个 promise 和两个 then 产生的 promise 对象发生的异常都会被最后一个 catch 捕获。但如果前面的 promise 发生异常后,后面的 then 方法不会被调用,而是直接调用最后的 catch 方法。上述代码使用了ES6中箭头函数,使代码更简洁,不清楚的同学请移步本系列的第一篇文章。
Promise.all
Promise.all
接收一个 promise 对象的数组作为参数,数组中的 promise 将会同时执行,当这个数组里的所有 promise 对象全部变为 fulfilled 状态的时候,它才会去调用 then 方法,并且会将返回值组成一个数组传递给 then 方法。
1 | var p1 = Promise.resolve(1); |
如果有变为 rejected 状态的 promise 对象,则立即会将第一个被 reject 的实例的返回值传递给 catch 方法。
1 | var p1 = new Promise(function (resolve, reject) { |
Promise.race
它的使用方法和 Promise.all
一样,接收一个 promise 对象数组为参数。Promise.all
在接收到的所有的对象promise都变为 FulFilled 或者 Rejected 状态之后才会继续进行后面的处理, 与之相对的是 Promise.race
只要有一个 promise 对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。
1 | var p1 = Promise.resolve(1); |
async 函数
ES7提供了 async 函数,使得异步操作变得更加方便。async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。正常情况下, await 命令后面是一个 Promise 对象。如果不是,会被转成一个立即 resolve 的 Promise 对象。
1 | function f() { |