async
异步关键字,一般我们把这个关键字写在函数前,用于表示函数是一个异步函数, 异步函数也就意味着该函数的执行不会阻塞后面代码的执行。
声明async函数的几个方法:
- 普通的函数声明
async function A(){}
- 声明一个函数表达式
let A = async function(){}
- async形式的箭头函数
let A = async ()=>{}
异步函数调用也跟普通函数调用一样
async function A(){}
console.log(A());
console.log('虽然在后面,但是我先执行');
我们可以看到,异步函数是不会阻塞下面代码的执行,并且异步函数返回了一个promise 对象,promise 对象是可以返回值和抛出错误的:
async function A(val){
if (val) {
return '123'
} else {
throw '错误'
}
}
console.log(A(true));
console.log(A(false));
再使用then和catch捕获返回值和错误信息。
await
等待关键字,意思是等待,等待后面的表达式执行完再继续执行下面的代码,这个作用的好处就是能大大减少回调地狱。后面可以放置任何表达式,但是更多的是放一个返回promise 对象的表达式。注意await 关键字只能放到async 函数里面。
举个例子,有个场景,需要调用a方法调取接口数据,等到获取数据之后再执行b方法,如果这时候不使用await,那就是需要编写回调方法,当我们再多几步这样的操作就很容易陷入回调地狱。
a(() => {
b(() => {
c();
});
});
await a();
await b();
c();
修改代码后确实简洁很多,但是如果滥用也很造成性能问题,如果ac方法是并行的,简单粗暴使用await会拉低整体执行的时间。
a(() => {
b();
});
c(() => {
d();
});
// 改为
await a();
b();
await c();
d();
改了之后虽然看起来是b等待a执行完,d等待c执行完,但是实际上d需要等ac都执行完才能执行,白白增加了等待的时长。
这种情况需要使用Promise.all()或者把两个执行完全隔离开来。
所以,await的使用也需要考虑具体的执行逻辑。
我们再来看一个串行并行的情况:
async function asyncAwaitFn(str) {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(str)
}, 1000);
})
}
const serialFn = async () => { //串行执行
console.time('test')
console.log(await asyncAwaitFn('string 1'));
console.log(await asyncAwaitFn('string 2'));
console.timeEnd('test')
}
serialFn();
async function asyncAwaitFn(str) {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(str)
}, 1000);
})
}
const zhix= async () => { //并行执行
console.time('zhix')
const test1 = asyncAwaitFn('string 1');
const test2 = asyncAwaitFn('string 2')
//直接打印
console.log(await test1)
console.log(await test2)
console.timeEnd('zhix')
}
zhix()
相比串行,并行执行的效率快了差不多一倍。如果多个串行执行改为并行,能大大提升执行速度。