try catch能捕获异步任务里的错误吗?

1,414 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

Javascript 是一门单线程的语言,任务是一个接一个的执行的,如果遇到耗时任务那么就会阻碍后面任务的执行,因此为了提高执行效率,Javascript采用异步回调的方式来执行耗时任务。

当遇到耗时任务时,不会等待任务执行完毕后才执行后面的任务,而是直接往后执行其他任务,当耗时任务执行完成后,通过事件循环来执行耗时任务对应的回调函数。

想象一下,异步任务执行时会存在错误的情况,那么怎么进行错误的捕获呢?

对于promise,我们可以使用catch来进行捕获。另外,我们也可以使用fail之类的回调方法供我们对错误结果进行处理。

那能不能使用try catch捕获异步任务呢?

setTimeouttry catch错误捕获

这里的setTimeout指的是一类,包括setTimeoutsetInterval这类所谓宏任务,那么可以用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(上下文)已经完全不同了。

Promisetry catch错误捕获

promise 也是一个异步的处理过程,它对应事件循环中的微任务。 其实promise上面的setTimeout存在同样的问题。

try {
  new Promise((resolve, reject) => {
    reject('promise error')
  })
} catch (e) {
  console.log('能catch到吗?', e)
}

catch不到,原因其实与上面的setTimeout是一样的,执行栈上下文已经不同了。

针对Promise,官方提供了一个方法catch, 通过catch能捕获到错误,从而可以阻止错误被抛到全局,导致程序崩溃

既然我用promisecatch不过到了错误,是不是就可以让外层的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捕获异步任务中的错误了。