前言
📫 大家好,我是南木元元,热爱技术和分享,欢迎大家交流,一起学习进步!
🍅 个人主页:南木元元****
背景
之前面某物的时候,遇到了一个有关try...catch的问题,让我印象深刻,这里来记录分享一下。
面试官:下面代码有什么问题吗?
示例1:
try {
setTimeout(() => {
throw new Error('err');
}, 200);
} catch (err) {
console.log(err);
}
示例2:
try {
Promise.resolve().then(() => {
throw new Error('err');
});
} catch (err) {
console.log(err);
}
第一反应是,这不就是一个普通的try...catch捕获错误吗?但其实是有坑在里面的。
js中的try...catch
js中的try...catch是平时写代码时经常会使用的,它可以捕获代码中的异常并防止应用程序崩溃。
try {
// 可能会抛出异常的代码
} catch(error) {
// 处理所有异常的代码
}
但try...catch并不能捕获所有的异常,这就需要了解try...catch的运行机制,这样才能保证我们合理地去使用它。
try...catch运行机制
当程序运行到try...catch里面时:
- 如果未报错,则会忽略catch中的代码
- 若报错,则不执行try报错内容后面的代码,转而执行catch中的代码
总结一下,能被try...catch捕捉到的异常,必须是在报错的时候,线程执行已经进入try...catch 代码块,且处在 try...catch 里面,这个时候才能被捕捉到。
js的事件循环机制
js是单线程语言,事件循环是js的执行机制。
- 所有同步任务都在主线程上执行,形成一个执行栈
- 在执行同步任务的时候,如果遇到了异步事件,会将该任务挂起,继续执行同步任务,当异步事件执行完后(如定时器到时,ajax请求返回),再将对应的回调加入到一个任务队列中等待执行,任务队列可以分为宏任务队列和微任务队列
- 当执行栈中的同步任务执行完毕后,会执行所有微任务,清空微任务队列
- 当执行完所有微任务后,再去执行宏任务队列中的下一个宏任务,不断循环,直到所有任务都完成。
这一套流程,就是事件循环。
错误原因
现在回到上述代码,其实就能够明白存在的问题是try...catch无法捕获异步错误。
try...catch是同步执行的,而setTimeout和Promise.resolve()一个是宏任务,一个是微任务,都是异步任务,等到setTimeout和Promise.resolve()里面的事件进入到事件队列的时候,主线程已经离开了try...catch,所以try...catch无法捕获异步错误。
解决方法
只需在同步任务中使用try...catch即可,利用Promise和async/await的内在能力。
对于第一个示例:
new Promise((resolve, reject) => {
setTimeout(() => {
try {
throw new Error('err');
} catch (err) {
reject(err);
}
}, 200);
})
.then(() => {
// 处理成功执行的情况
})
.catch((err) => {
console.log(err); // 错误在这里被捕获
});
对于第二个示例:
// 方法一:使用Promise链式调用
Promise.resolve()
.then(() => {
throw new Error('err');
})
.catch((err) => {
console.log(err); // 错误在这里被捕获
});
// 方法二:使用async/await
async function handleError() {
try {
await Promise.resolve().then(() => {
throw new Error('err');
});
} catch (err) {
console.log(err); // 错误在这里被捕获
}
}
handleError();
结语
因此,在代码中不要再随便写try...catch了,异步错误是无法被捕获的,而且像Promise有它自己的异常捕获方法,比try...catch更好用。
🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下博主~
版权归原作者 南木元元 所有, 如有侵权,请联系我们删除。