关于JS的异常捕获,主要分为两种,一种为同步情况下的异常捕获,一种为一步执行下的异常捕获;异常捕获的【坑】主要集中在异步场景。
在同步场景下,简单粗暴,直接使用try/catch解决问题。
(function() {
try {
// catch: ReferenceError: obj is not defined
console.log(obj.error)
} catch (e) {
console.log('catch:', e)
}
})();
catch: ReferenceError: obj is not defined
at <anonymous>:4:17
at <anonymous>:8:3
在异步场景下,按照上面的异常捕获方式是无法捕获到异常的,如下
// 无法捕获到异常
(function() {
try {
setTimeout(function() {
console.log(obj.error)
}, 500)
} catch (e) {
console.log('catch:', e)
}
})()
Uncaught ReferenceError: obj is not defined
at <anonymous>:4:19
at i (init.js:1)
之所以无法捕获到异常,原因在于异步方法执行时,主流程已执行完毕,try/catch已经退出函数调用栈;正确的异常捕获如下:
(function() {
try {
setTimeout(function() {
try {
console.log(obj.error)
} catch (e) {
// 此处捕获到异常
console.log('catch1:', e)
}
}, 500)
} catch (e) {
// 此处无法捕获到异常
console.log('catch2:', e)
}
})()
catch1: ReferenceError: obj is not defined
at <anonymous>:5:21
at i (init.js:1)
从这里可以看出,最外层的try/catch是不生效的,可以去掉。
首先通过两端代码来看promise的异常捕获情况
代码一:
(function() {
try {
const promise = new Promise((resolve, reject) => {
setTimeout(function() {
// 此处的异常无法被捕获
console.log('1', obj.error)
resolve(100)
})
})
promise.then(result => {
console.log('result:', result)
}).catch(error => {
// 此处无法捕获到异常
console.log('catch1:', error)
})
} catch (e) {
// 此处无法捕获到异常
console.log('catch2:', e)
}
})();
Uncaught ReferenceError: obj is not defined
at <anonymous>:6:26
at i (init.js:1)
代码二:
(function() {
try {
const promise = new Promise((resolve, reject) => {
console.log(obj.error)
setTimeout(function() {
resolve(100)
})
})
promise.then(result => {
console.log('result:', result)
}).catch(error => {
// 此处捕获到异常
console.log('catch1:', error)
})
} catch (e) {
// 此处无法捕获到异常
console.log('catch2:', e)
}
})()
catch1: ReferenceError: obj is not defined
at <anonymous>:4:19
at new Promise (<anonymous>)
at <anonymous>:3:21
at <anonymous>:19:3
总结:
- 最外层的try/catch对于promise中的异常捕获完全无效
- new Promise()中的异步异常(setTimeout)只能在内部捕获
- new Promise()中的同步异常只能通过catch捕获
核心:熟悉EventLoop就知道,每个任务使用独立的函数调用栈;所以,每一个task都需要单独捕获异常;使用promise.catch能够捕获到promise任务的异常。
追寻talk is cheap,show me the code
的原则,这里直接上验证代码,让实际结果来说明一切。
function f() {
return new Promise((resolve, reject) => {
console.log(obj.error)
setTimeout(function() {
// 1.这里的异常只能在setTimeout内部使用try/catch捕获
// console.log(obj.error)
resolve(100)
})
})
}
async function main() {
try {
const result = await f().catch(e => {
// 2.优先捕获到异常
console.log('catch1:', e)
})
console.log(result)
} catch (e) {
// 3.如果没有catch1,这里也能捕获到异常
console.log('catch2:', e)
}
}
main()
总结:
- new Promise()中的异步异常(setTimeout)只能在内部捕获
- 可以使用
.catch
也可使用try/catch
来捕获异常,其中.catch
优先级较高
作者公众号: