一文搞定JS异常捕获

  关于JS的异常捕获,主要分为两种,一种为同步情况下的异常捕获,一种为一步执行下的异常捕获;异常捕获的【坑】主要集中在异步场景。

同步

  在同步场景下,简单粗暴,直接使用try/catch解决问题。

1
2
3
4
5
6
7
8
9
10
11
12
(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

异步

setTimeout异常

在异步场景下,按照上面的异常捕获方式是无法捕获到异常的,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 无法捕获到异常
(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已经退出函数调用栈;正确的异常捕获如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(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异常

首先通过两端代码来看promise的异常捕获情况
代码一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(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)

代码二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
(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

总结:

  1. 最外层的try/catch对于promise中的异常捕获完全无效
  2. new Promise()中的异步异常(setTimeout)只能在内部捕获
  3. new Promise()中的同步异常只能通过catch捕获

核心:熟悉EventLoop就知道,每个任务使用独立的函数调用栈;所以,每一个task都需要单独捕获异常;使用promise.catch能够捕获到promise任务的异常。

async/await异常

追寻talk is cheap,show me the code的原则,这里直接上验证代码,让实际结果来说明一切。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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()

总结:

  1. new Promise()中的异步异常(setTimeout)只能在内部捕获
  2. 可以使用.catch也可使用try/catch来捕获异常,其中.catch优先级较高

本文首发于公众号

留言

欢迎交流想法。留言会通过 GitHub Issues 保存,首次使用需要登录 GitHub。