前端中 try-catch 捕获不到哪些异常和错误

我们经常会使用try-catch模块来主动捕获一些异常或者错误,避免此块的代码影响到其他模块或者整体代码的运行。但有些情况,try-catch 并不能捕获到代码中的异常!

1 跨域的错误

当我们使用 xhr 请求接口,若接口不支持跨域时,浏览器会在控制台提示错误:

Access to XMLHttpRequest at 'https://xxxxxxx.qq.com/qq/userInfo' from origin 'https://www.xiabingbao.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource

通过图片中我们可以看到,请求接口时产生了跨域错误,但并没有进入到catch中。

之前我在做 canvas 合成图片时,某些图片的域名不支持跨域,本来想的是先直接请求图片,如果图片跨域了,则请求后台接口,后台接口将该图片转为 base64。但在测试的过程中发现,这个跨域的错误,一定会展示出来。可是,我本来就不想展示给调用方,因为我已经设置了兜底的方案了。但用 try-catch 是兜不住这个错误的。

当然,这并不是说前端不知道产生了跨域的错误,我们通过 xhr.onerror 的监听,是可以知道 xhr 请求产生了请求错误的。

2 异步错误

如果try内部的异步发生错误,catch是接收不到的

setTimeout中的错误

try {
  setTimeout(function () {
    throw new Error('error in setTimeout'); // 200ms后会把异常抛出到全局
  }, 200);
} catch (err) {
  console.error('catch error', err); // 不会执行
}

Promise中的错误

try {
  Promise.resolve().then(() => {
    throw new Error('error in Promise.then');
  });
} catch (err) {
  console.error('catch error', err);
}

通过图片中的运行结果可以看到,这两种代码,均没有进入到 catch 模块中。

那么我们应该怎么捕获这种异步的错误呢?答案就是把 try-catch 放到异步代码的里面。

将try-catch放到setTimeout内部

setTimeout(() => {
  try {
    throw new Error('error in setTimeout');
  } catch (err) {
    console.error('catch error', err);
  }
}, 200);

将try-catch放到then内部

Promise.resolve().then(() => {
  try {
    throw new Error('error in Promise.then');
  } catch (err) {
    console.error('catch error', err);
  }
});

使用Promse自带的catch捕获异常

Promise.resolve()
  .then(() => {
    throw new Error('error in Promise.then');
  })
  .catch((err) => {
    console.error('Promise.catch error', err);
  });

Promise 有一个很大的优势是,它自带着异常捕获方法catch(),在 then()方法产生错误导致代码无法运行时,会自动进入到 catch()方法中。因此建议写 Promise 时,都把 catch()写上,否则未捕获的异常,就会冒泡到全局。

3 async-await 的异常如何捕获

async-await的语法糖,可以让我们像写同步代码一样写 Promise,那么在 async-await 中如何捕获异常呢?

这里我们通常就会使用try-catch来捕获异常了。

const request = async () => {
  try {
    const { code, data } = await somethingThatReturnsAPromise();
  } catch (err) {
    console.error('request error', err);
  }
};

当 somethingThatReturnsAPromise()方法产生 reject 的异常时,就会被 catch 捕获到。

当然,async-await 还有一种捕获异常的方式,在通过 await 返回正确数据时,还可以顺带写上catch()捕获异常,当 somethingThatReturnsAPromise()方法异常时,就会自动进入到 catch()方法中:

const request = async () => {
  try {
    const { code, data } = await somethingThatReturnsAPromise().catch((err) => console.error('catch error', err));
  } catch (err) {
    console.error('request error', err);
  }
};

但这种捕获异常后,外层的 catch()方法就捕获不到异常了,不再继续向外层冒泡了。正确的做法是,底层模块产生的错误,应当直接抛出给业务层,让业务层决定这个错误怎么处理,而不是直接吞掉。

4 多层 try-catch

多层 try-catch 时,会被最内层的 catch()方法捕获到,然后就不再向外层冒泡:

try {
  try {
    throw new Error('error');
  } catch (err) {
    console.error('内层的catch', err); // 内层的catch Error: error
  }
} catch (err) {
  console.error('最外层的catch', error);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值