关于JS的异常捕获,主要分为两种,一种为同步情况下的异常捕获,一种为一步执行下的异常捕获;异常捕获的【坑】主要集中在异步场景。
同步
在同步场景下,简单粗暴,直接使用try/catch解决问题。
1 2 3 4 5 6 7 8 9 10 11 12
| (function() { try { 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
|
总结:
- 最外层的try/catch对于promise中的异常捕获完全无效
- new Promise()中的异步异常(setTimeout)只能在内部捕获
- 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() { resolve(100) }) }) } async function main() { try { const result = await f().catch(e => { console.log('catch1:', e) }) console.log(result) } catch (e) { console.log('catch2:', e) } } main()
|
总结:
- new Promise()中的异步异常(setTimeout)只能在内部捕获
- 可以使用
.catch也可使用try/catch来捕获异常,其中.catch优先级较高
本文首发于公众号
留言
欢迎交流想法。留言会通过 GitHub Issues 保存,首次使用需要登录 GitHub。