关于写这篇博客的原因,是因为最近在工作中用try,catch捕获Promise错误时,有的时候捕获不到,有的时候又被promise.catch捕获,对其捕获机制和顺序并不是很明白,查阅了很多资料,也没有写的很透彻的资料,所以写篇博客,方便日后查阅
前言
平常撸代码的时候,try catch 用的太多了,特别是一些 ”安全感" 低的人,基本是到处 try catch,生怕 JS 报错,然后页面整个挂掉了。
为什么安全感低呢,因为界限模糊,很多新手前端不知道try,catch
捕获的是哪种异常,而且能否捕获Promise异常,以及如果Promise用.catch捕获,到底被谁捕获到
所以我们要弄清楚try catch,做一个安全感高的码农!!!
-
面试官:麻烦用一句话描述 JS 异常是否能被 try catch 到?
-
面试者:异步方法无法捕捉到……
-
面试官:不要背答案,麻烦用一句话描述 JS 异常是否能被 try catch 到!
-
面试者:沉默 ing …………
-
面试者:能捕捉到的异常必须是线程执行已经进入 try catch 但 try catch 未执行完的时候抛出来的。
-
面试官:沉默 ing …………
-
面试官:啥时候可以来上班?
-
欢笑交谈中,拿到 offer …………
关于trycatch的运行机制
当程序运行到try catch里面时,如果未报错,则忽略catch中的代码,若报错,则不执行try报错内容后面的代码,转而执行catch中的代码。
什么时候try catch 才能捕获到异常?
能捕捉到的异常必须是线程执行已经进入 try catch 但 try catch 未执行完的时候抛出来的
1,当语法错误时,不能捕获
因为语法错误是在语法检查阶段就报错了,线程执行尚未进入 try catch 代码块,自然就无法捕获到异常。 例如:
try{
a.
}catch(e){
console.log("error",e);
}
// output
Uncaught SyntaxError: Unexpected token '}'
2,语法正确,线程进入try catch中时,可以捕获
function d(){a.b;}
try{
d();
}catch(e){
console.log("error",e);
}
// output
error ReferenceError: a is not defined
代码执行进入了 try catch ,执行 d() 方法的时候,线程执行处在 try 里面,所以能捕捉到。
3,异步无法捕获
try{
setTimeout(()=>{
console.log(a.b);
}, 100)
}catch(e){
console.log('error',e);
}
console.log(111);
//output
111
Uncaught ReferenceError: a is not defined
因为,setTimeout是异步函数,而try catch其实是同步顺序执行的代码,等setTimeout里面的事件进入事件队列的时候,主线程已经离开了try catch,所以try catch是无法捕获异步函数的错误的。
4.Promise异常无法捕获
// 异步,微任务
try {
new Promise(() => {
throw new Error('new promise throw error');
});
} catch (error) {
console.log(error);
}
try-catch 主要用于捕获异常,注意,这里的异常,是指同步函数的异常,如果 try 里面的异步方法出现了异常,此时catch 是无法捕获到异常的,原因是因为:当异步函数抛出异常时,对于宏任务而言,执行函数时已经将该函数推入栈,此时并不在 try-catch 所在的栈,所以 try-catch 并不能捕获到错误。对于微任务而言,比如 promise,promise 的构造函数的异常只能被自带的 reject 也就是.catch 函数捕获到。
如果想要捕获Promise异常
- 1.Promise异常并不是绝对不能被捕获到的,如下
async function fn() {
try {
await new Promise(() => {
throw new Error('new promise throw error');
});
} catch (error) {
console.log(error);
}
}
fn()
代码运行结果
这次Promise异常能被捕获到,是因为async和await
,正常不加async,await的时候,执行promise后,在等待promise回调的时候,try,catch已经执行完了,所以捕获不到,然而加了async和await后,try,catch必须等promise的回调执行完后,才能继续往下走,这个时候trycatch没执行完,promise抛出异常,自然而然能被catch捕获到
- 2.关于promise异常到底是被catch捕获还是.catch捕获 接下来我们把代码修改一下
async function fn() {
try {
await new Promise(() => {
throw new Error('new promise throw error');
}).catch(error=>{
console.log('.catch',error);
})
} catch (error) {
console.log('try,catch',error);
}
}
fn()
.catch大家都知道,用来捕获promise异常,当我们有了.catch,错误在进入catch之前,就被.catch捕获到了,所以不会再往下走(可能有点绕,.catch是Promise异常捕获机制,不带. 是try,catch)
代码运行结果如下
到这里,我们可以发现,这个错误被.catch捕获到了,那如果我们不抛出一个错误,直接返回一个reject 会怎么样呢
async function fn() {
try {
const res = await new Promise((resove,reject) => {
return reject('捕获到了')
}).catch(error=>{
console.log('被promise.catch',error);
})
} catch (error) {
console.log('被try,catch',error);
}
}
fn()
运行结果
Promise异常,如果写了.catch,就会被优先.catch捕获到,如果没有,那么就会被try,catch捕获到,
总结
通过上面分析,我们得出能被 try catch 捕捉到的异常,必须是在报错的时候,线程执行已经进入 try catch 代码块,且处在 try catch 里面,这个时候才能被捕捉到。 并且,try,catch如果使用async和await,也是可以捕获到Promise异常的,但我们不推荐,毕竟,Promise异常有它自己的.catch来捕获,而且更好用,不是吗?
所以,没安全感的兄弟们,看完这篇文章,不要再到处写trycatch了,我们自己写的代码,没我们想的这么脆弱
如果上述文章有哪里不对或需要改进,可以再底下评论或私信,一起学习交流
编辑不易,个位看官老爷,点个赞呗!!!!!!
参考文献