es6中引入的async函数为开发者提供了一种功能更加强大方便的异步操作方案。
1.1
创建async函数与普通函数的区别就是,async函数需要使用async关键字进行创建。async就是异步的意思。
function a () {
return new Promise((resolve,reject) => {
setTimeout(() => resolve('2'), 1000);
})
}
async function fn () {
let p = await a();
console.log(p)
}
fn() //2
上面的例子中,会在1秒钟之后打印出2。
先看a函数,a函数里面返回了一个promise对象,在一秒钟之后,会把promise设置为成功的状态。
在下面,我们又创建了一个async函数,在async函数中,可以看到一个await关键字,await就是等待的意思。这里的效果是,会等待a函数中返回的promise对象的状态为成功时,才会继续执行函数体内下面的代码。(async函数体外的代码不受影响),所以一秒钟之后打印出了2。
await关键字右边应该是一promise对象,如果不是,会被转换成一个立即resolve的promise对象。
由此你会发现,async函数体内的异步操作变得像是同步的一样,想想回调函数的回调地狱,或者单纯使用promise,那种then方法的链式调用。async函数的方式是不是更加简便呢?我们可以来看这三种方式的例子。
比如我们需要完成三个表单的验证,第一个验证完毕后才能进行下一个表单的验证。
回调函数式:
function fn1 () {
//发起第一个表单的验证,验证成功后发起第二个表单的验证
function fn2 () {
//完成第二个表单的验证后,发起第三个表单的验证
function fn3 () {
}
}
}
promise方式:
fn1() //第一个表单验证成功
.then(res => fn2()) //第二个表单验证成功
.then(res => fn3()) //第三个表单验证成功
async函数方式:
async function func () {
await fn1();
await fn2();
await fn3();
}
1.2
async函数会返回一个promise对象。因此可以是以then方式添加回调。async函数内部返回的值,会作为参数传递给then方法中的回调函数。
function a () {
return new Promise((resolve,reject) => {
setTimeout(() => resolve(2), 1000);
})
}
async function fn () {
let p = await a();
return p+1;
}
fn().then(res => console.log(res)) //3
需要注意的是,async函数返回的promise对象必须等待函数内部的所有await后面的promise对象的状态都改变为resolve的时候,自身的状态才会改变为resolvue,然后执行then方法中的回调。
如果是遇到return语句的时候,对象的状态会立即改变。当抛出错误,或者其中一个await后面的promise对象状态变更为reject,那么async函数返回的promise对象的状态会被立即设置为reject状态。对抛出的错误对象,可以通过catch方法中的回调进行接收。如下:
async function fn () {
throw new Error('出错了');
}
fn().catch(err => console.log(err)) //出错了
只要有一个await命令后面的promise的状态变为reject,那么整个async函数都会中断执行,并且reject的参数会被catch方法中的回调函数接收。
function a () {
return new Promise((resolve,reject) => {
setTimeout(() => reject('失败了'), 1000);
})
}
async function fn () {
await a();
console.log(1)//不执行
}
fn().catch(err => console.log(err)) //失败了
上面的代码中,由于a函数中返回的promise对象的状态是失败的,导致后面的代码不执行,而reject的参数被catch函数中的回调函数接受到,打印出失败了。
如果不希望因为上一个promise对象状态的失败导致后面的代码不执行。可以把代码放在try,catch语句里。
function a () {
return new Promise((resolve,reject) => {
setTimeout(() => reject('失败了'), 1000);
})
}
async function fn () {
try {
await a();
}catch(e) {
console.log(e)//失败了
}
console.log(1)//1
}
fn()
上面代码中,try、catch语句中的promise的reject的参数被catch接受到,并且后面的代码没有中断,依旧执行。下面是抛出错误的情况下:
function a () {
return new Promise((resolve,reject) => {
throw new Error('出错了');
})
}
async function fn () {
try {
await a();
}catch (e) {
console.log(e) //出错了
}
console.log(1) //1
}
fn()
1.3 注意不要将没有依赖关系的异步任务写成继发的方式。
async function fn () {
await fn1();
await fn2();
}
上面函数中的fn1和fn2异步任务没有依赖关系,但被写成继发的方式,因为只有fn1的promise状态改变了,后面的代码才能运行,所以,这两个异步任务应当改成同时出发。
方式一:
async function fn () {
let [a,b] = Promise.all( [fn1(), fn2()] );
}
方式二:
async function fn () {
let a = await fn1();
let b = await fn2();
let f1 = await a;
let f2 = await b;
}