前言
大家好,我是JS-Man
做前端开发也好几年了,相信大家对async、await肯定不陌生,在写业务代码的时候经常要用到,这篇文章分享下我在开发过程中遇到的两个疑问:
- await没有按照我的预期工作,程序没有暂停?
- async里都是用try catch进行捕获,我不用行不行?
下面让我们带着这俩问题开始这篇文章
async是语法糖
什么是async,一句话概括,它就是Generator函数的语法糖,是根据执行器可中断的生成器函数,async简化了*和yield,更便于我们阅读
并且不需要执行器,直接就可以运行,如下代码就是最简单的async函数使用方法:
async function fn1() {
// 异步逻辑
}
async function fn2() {
// 异步逻辑
}
async function main() {
await fn1();
await fn2();
}
main();
暂停执行的条件
ok,那现在我们知道了async其实就是Generator函数,那await后面执行的逻辑肯定就是需要状态才能继续往下执行另一段函数的。
看下面这段代码:
async function fn1(params) {
setTimeout(() => {
console.log(11);
return true;
}, 2000);
}
async function main() {
await fn1();
console.log(22);
}
main();
大家觉得是先打印11还是22?
其实问题点就在于await fn1()会不会暂停,等待fn1状态变更后再继续执行下面的逻辑,如果main不是async函数,是一个普通函数的话,按照宏任务微任务机制,应该是先打印22,但是main现在是async函数,理论上要等fn1执行完过两秒打印11后再执行下面的打印22。
其实上面代码是错误的,fn1不会暂停函数逻辑2s,哎?为什么呢,不是说async返回的是promise吗,那fn1就应该等待2s才有返回值呀。
async确实返回的promise,但如果不返回promise,直接return基本数据类型的话,promise状态是fulfilled,所以main是默认此段函数的异步状态是完成的,所以不会暂停,而正确的代码如下:
function fn1(params) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(11);
resolve(true);
}, 2000);
});
}
async function main() {
await fn1();
console.log(22);
}
main();
fn1里返回一个promise,此时fn1的状态就是pending,等待2s后resolve(true),异步状态就是fulfilled,此时在main里这段代码执行完毕,继续往下走。
不用try catch行不行
不知道大家有没有过疑惑,为什么async里要使用try catch进行异常捕获,我看try catch杂乱,就是不想写try catch行不行,我们来看如下代码:
function fn1(params) {
return new Promise((resolve, reject) => {
setTimeout(() => {
throw new Error("我出错了!");
}, 2000);
});
}
async function main() {
await fn1();
console.log(22);
}
main();
fn1里抛出了一个异常,看下控制台,可以发现main函数直接中断了,没有再打印22,要是放在真实场景中,那这不完犊子了吗,我们肯定是要对这个错误进行捕获并处理。
这里大家就清楚try catch在async里的作用了,但是!我还是不想写try catch,那简单,如下:
function fn1(params) {
return new Promise((resolve, reject) => {
throw new Error("我出错了!");
});
}
async function main() {
await fn1().catch((err) => {
console.log(err);
});
console.log(22);
}
main();
我写个catch捕获下错误就行了,这里请注意,我去除了上例中的setTimeout,因为在setTimeout回调里抛出错误,不在promise的执行上下文内,promise是捕获不到的,加上setTimeout的话,得拿reject或者trycatch进行捕获,才能触发catch函数。
总结
本篇文章对async进行了剖析,通过分析async的暂停条件,降低了大家对async的使用负担,通过以上研究,回答在文章开始提出的两个问题:
- 在async里,一段逻辑暂停的条件是异步状态从pending变为fulfilled或rejected。
- 可以不使用try catch,但得加上错误捕获,不然会中断程序执行,会发生生产事故。