持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
Javascript
是一门单线程的语言,任务是一个接一个的执行的,如果遇到耗时任务那么就会阻碍后面任务的执行,因此为了提高执行效率,Javascript
采用异步回调的方式来执行耗时任务。
当遇到耗时任务时,不会等待任务执行完毕后才执行后面的任务,而是直接往后执行其他任务,当耗时任务执行完成后,通过事件循环来执行耗时任务对应的回调函数。
想象一下,异步任务执行时会存在错误的情况,那么怎么进行错误的捕获呢?
对于promise
,我们可以使用catch
来进行捕获。另外,我们也可以使用fail
之类的回调方法供我们对错误结果进行处理。
那能不能使用try catch
捕获异步任务呢?
setTimeout
的try catch
错误捕获
这里的setTimeout
指的是一类,包括setTimeout
, setInterval
这类所谓宏任务,那么可以用try catch
来捕获错误吗?
try {
setTimeout(() => {
let a = c
}, 100)
} catch (e) {
console.log('能获取到错误吗?', e)
}
执行上面代码后程序直接报错,这可能会导致整个页面挂了;或者在nodejs
项目中中,整个服务挂了。 我们的初心是想让程序更加健壮,但却做了无用功。
既然在setTimeout
外边无法获取,那能不能在setTimeout
里面先用try catch
获取一下,然后捕获到错误后再传出去呢?
try {
setTimeout(() => {
try {
let a = c
} catch (e) {
throw new Error('c is not defined')
}
}, 100)
} catch (e) {
console.log('能获取到错误吗?', e)
}
结果是外边也catch不到。
try catch
捕获不到异步任务里面的错误的根本原因事件循环导致的。事件循环不是一句空话,而是在代码中实实在在存在的。
这个错误跟最外层的try catch
并不在一个执行栈中,当执行异步任务里面的回调时,外层执行栈里面的任务早已执行完,它们的context
(上下文)已经完全不同了。
Promise
的try catch
错误捕获
promise
也是一个异步的处理过程,它对应事件循环中的微任务。 其实promise
上面的setTimeout
存在同样的问题。
try {
new Promise((resolve, reject) => {
reject('promise error')
})
} catch (e) {
console.log('能catch到吗?', e)
}
也catch
不到,原因其实与上面的setTimeout
是一样的,执行栈上下文已经不同了。
针对Promise
,官方提供了一个方法catch
, 通过catch
能捕获到错误,从而可以阻止错误被抛到全局,导致程序崩溃。
既然我用promise
的catch
不过到了错误,是不是就可以让外层的catch也捕获到呢?
try {
new Promise((resolve, reject) => {
reject('promise error')
}).catch(e => {
throw new Error(e)
})
} catch (e) {
console.log('能catch到吗?', e)
}
结果还是不行,原因还是不在一个执行栈上下文中。
既然都捕获不到,那怎么办呢?官网提供了async await
。
async await
在async await
中能用try catch
捕获到异步任务中的错误吗?
const asyncFn = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('asyncFn执行时出现错误了')
}, 100)
})
}
const executedFn = async () => {
try {
await asyncFn()
} catch (e) {
console.log('拦截到错误', e)
}
}
executedFn()
执行后,发现错误被catch
到了。
asyncFn
里面是有Promise
,为什么外边就能catch
到了呢?这不是跟上面讲的相矛盾吗?因为它们都不在一个执行上下文了。
要想搞清楚这个问题,就需要要弄懂async await
,可以参考这篇文章深刻理解async/await。
async await
利用生成器Generator
(协程)和Promise
实现的,它利用了Generator
切换上下文的功能。
await
能存储当前执行的上下文,当执行到await
后面的代码时会创建一个新的执行上下文来执行异步任务。当异步任务执行完后,await
又切换到之前的上下文,这样就会把错误从一个上下文中抛到正在执行的上下文中,不至于抛到全局,导致程序崩溃。
所以说,async await
能够回到最外层的执行上下文,这样就能使用try catch
捕获异步任务中的错误了。