关于async/await
- 声明一个 async 方法
// 普通的函数声明
async function foo(){}
//声明一个函数表达式
const foo = async function(){}
// async 形式的箭头函数
const foo = async ()=>{}
- async 的返回值 async 函数总是会返回一个 Promise 对象
如果 return 关键字后面不是一个 Promise,那么默认调用 promise.resolve 方法进行转换
async function asyncFunction (){
return 'Hello World'
}
asyncFunction.then((data)=>{
console.log(data) // Hello World
})
上面的asyncFunction虽然返回了字符串,却能用 then 方法来获得最终值,这是内部将字符串转换成了 Promise 的缘故。
- async 函数的执行过程
- 执行开始
- 自动生成一个Promise
- 执行中
- 如果遇到return/throw 执行立刻退出
- 如果遇到await 会暂停执行,但不会阻塞主线程,直到await后面的异步操作结束后恢复执行
- 执行完毕
- 无论是否使用 await,异步函数都会返回 Promise
最佳实践
虽然async可以让代码看起来像同步代码,但是也不要错过了并行的机会,示例如下,wait 为异步代码,实际生产中可以是一个接口请求等操作。
bad : 最少1000毫秒执行完毕
async function series() {
await wait(500);
await wait(500); // 等待上一个异步结束后执行
return "done!"; // 累计耗时1000毫秒+
}
good : 最少500毫秒执行完毕
async function parallel() {
const wait1 = wait(500); // 执行异步
const wait2 = wait(500); // 执行异步
await wait1;
await wait2;
return "done!"; // 所有异步接受后 累计耗时500毫秒+
}
其他
不要为了await而await ,注意原本的代码业务逻辑,合理利用并行的情况。 比如有如下的异步代码(回调形式)
a(() => {
b();
});
c(() => {
d();
});
改用async 的方式, 如果写成
await a();
await b();
await c();
await d();
用回调再翻译回来就是
a(() => {
b(()=>{
c(()=>{
d()
})
});
});
偏离了原来的业务逻辑 ,变成了串行,继续优化async
const asyncA = a()
const asyncC = c()
await asyncA
b()
await asyncC
d()
虽然a和c 并行了,但是原本d只要c结束就可以执行,但是如果a的速度晚于c,就变成了
a(()=>{
d()
})
也就是d需要等待的时间为a和c的最大值,继续优化async,最终可以通过如下形式还原业务
(async () => {
await a()
b()
})()
(async () => {
await c()
d()
})()
异常捕获 一般情况可能会通过try catch 捕获,但是如下情况的异常是捕获不到的
async function catchDemo() {
try {
// 注意这里是return,不是await
return Promise.reject(new Error("Oops!"));
} catch (error) {
// 代码不会执行到这里
}
}
catchDemo()
由于async运行的结果是返回一个Promise,所以可以通过.catch捕获
catchDemo().catch((error)=>{
console.log(error)
})
参考资料: