title: try…catch
categories: Javascript
tag:

  • 捕获异常
    date: 2021-12-25 17:11:34

try/catch语句

js红宝书中,关于try/catch的解释

  1. try{
  2. }catch(error){
  3. console.log(error)
  4. }

如果try块中有代码发生错误,代码会立即退出执行,并跳到catch中。

catch块此时接收到一个对象,该对象包含发生错误的相关信息,与其他语言不同,即使在catch块中不使用错误对象,也必须为它定义名称。错误对象中暴露的实际信息因浏览器而异,但至少包含保存错误消息的message属性。

异常处理的必要

增强用户体验,准确定位问题,完善的前端处理方案,比如前端监控系统

异常处理的场景

JS 语法错误、代码异常

Promise 异常

Iframe 异常

跨域异常

接口请求AJAX 请求异常

静态资源导入加载异常

捕获哪些异常

try/catch语句能够捕获的异常是什么?

我们可以把JS代码运行分为三个阶段。

try catch之前,之中,之后。

try/catch之前

代码报错的时候,线程执行未进入 try catch,那么无法捕捉异常。

比如语法异常(syntaxError),因为语法异常是在语法检查阶段就报错了,线程执行尚未进入 try catch 代码块,自然就无法捕获到异常。

  1. try {
  2. a.
  3. } catch (e) {
  4. console.log('error', e)
  5. }
  6. // output
  7. //SyntaxError: Unexpected token '}'

try/catch之中

代码报错的时候,线程执行处于 try catch 之中,则能捕捉到异常。

  • 方法和执行都在 try 里面,能捕捉到异常。
  1. try {
  2. function d() {
  3. a.b
  4. }
  5. d()
  6. } catch (e) {
  7. console.log('error', e)
  8. }
  9. // output
  10. //error ReferenceError: a is not defined
  • 方法定义在外部,执行方法在 try 里面,能捕捉到异常
  1. function d() {
  2. a.b
  3. }
  4. try {
  5. d()
  6. } catch (e) {
  7. console.log('error', e)
  8. }
  9. // output
  10. //error ReferenceError: a is not defined

上述报错的时机,都是代码执行进入了 try catch ,执行 d() 方法的时候,线程执行处在 try 里面,所以能捕捉到。

try/catch之后

  1. try {
  2. setTimeout(() => {
  3. console.log(a.b)
  4. }, 100)
  5. } catch (e) {
  6. console.log('error', e)
  7. }
  8. console.log(111)
  9. //output
  10. /*
  11. 111
  12. Uncaught ReferenceError: a is not defined
  13. */

setTimeout 里面报错,实际上是 100ms 之后执行的代码报错,此时代码块 try catch 已经执行完成,111 都已经被执行了,故无法捕捉异常。

  1. try {
  2. function d() {
  3. a.b
  4. }
  5. } catch (e) {
  6. console.log('error', e)
  7. }
  8. console.log(111)
  9. d()
  10. //output
  11. /*
  12. 111
  13. Uncaught ReferenceError: a is not defined
  14. */

方法定义在 try catch 代码块里面,但是执行方法在 try catch 外,在执行 d 方法的时候报错,此时 try catch 已经执行完成,111 都已经被执行了,故而无法捕捉异常。

总结

能被 try catch 捕捉到的异常,必须是在报错的时候,线程执行已经进入 try catch 代码块,且处在 try catch 里面,这个时候才能被捕捉到。

如果是在之前,或者之后,都无法捕捉异常。

Promise没有异常

相对于外部 try catch,Promise 没有异常!

  1. try {
  2. new Promise(function (resolve, reject) {
  3. a.b
  4. }).then((v) => {
  5. console.log(v)
  6. })
  7. } catch (e) {
  8. console.log('error', e)
  9. }
  10. //output
  11. // (node:10984) UnhandledPromiseRejectionWarning: ReferenceError: a is not defined

看如上报错,线程在执行 a.b 的时候,事实上属于同步执行,try catch 并未执行完成,按理应该能捕捉到异常,这里为啥无法捕捉呢?

事实上,Promise 的异常都是由 reject 和 Promise.prototype.catch 来捕获,不管是同步还是异步。

核心原因是因为 Promise 在执行回调中都用 try catch 包裹起来了,其中所有的异常都被内部捕获到了,并未往上抛异常。

如下来自 Promises/A+ 的实现 then/promise 源码:

  1. function getThen(obj) {
  2. try {
  3. return obj.then;
  4. } catch (ex) {
  5. LAST_ERROR = ex;
  6. return IS_ERROR;
  7. }
  8. }
  9. function tryCallOne(fn, a) {
  10. try {
  11. return fn(a);
  12. } catch (ex) {
  13. LAST_ERROR = ex;
  14. return IS_ERROR;
  15. }
  16. }
  17. function tryCallTwo(fn, a, b) {
  18. try {
  19. fn(a, b);
  20. } catch (ex) {
  21. LAST_ERROR = ex;
  22. return IS_ERROR;
  23. }
  24. }

可以看到,这里执行 then (Promise.prototype.then 回调), tryCallTwo (doResolve 回调), tryCallOne (handleResolved 回调) 方法都被 try catch了。

异常都被包裹起来了。所以异常都不会被外层的 try catch 捕捉,因此在外层的 try catch 看来,Promise 根本没有异常,事实上也确实没有“异常”,比如:

  1. try{
  2. new Promise(function (resolve, reject) {
  3. a.b;
  4. }).then(v=>{
  5. console.log(v);
  6. });
  7. console.log(111);
  8. }catch(e){
  9. console.log('error',e);
  10. }
  11. console.log(222);
  12. // output
  13. 111
  14. 222
  15. Uncaught (in promise) ReferenceError: a is not defined

显然,a.b 报错之后的,111 和 222 都能正常运行,promise 的异常都已经被内部 catch 了,在外层的 try catch 看来就是没有异常,线程继续执行。

try catch 无法捕捉 Promise 的异常,是因为 Promise 的异常没有往上抛。

再看一例:

  1. function a(){
  2. return new Promise((resolve, reject) =>{
  3. setTimeout(() => {
  4. reject(1);
  5. })
  6. })
  7. }
  8. try{
  9. await a();
  10. }catch(e){
  11. console.log('error',e);
  12. }
  13. console.log(111);
  14. //output
  15. error 1
  16. 111
  17. 复制代码

这个例子的异常被 catch 捕获到了,那么这里的 Promise 为啥能捕获到异常呢?

我们还是看开始的“一句话总结”

报错的时候( setTimeout 里面的 reject ),线程执行已经进入 try catch 代码块,但是并未执行完成,这样的话当然可以捕获到异常。await 将代码执行停留在 try catch 代码块里面的缘故。

敲黑板了: 不要用 try catch 包裹 Promise , Promise 很强大,不用担心异常会往上抛!我们只需要给 Promise 增加 Promise#catch 就 OK 了

try/catch/finally

**try**语句允许定义在执行时进行错误测试的代码块。

**catch** 语句允许定义当 **try** 代码块发生错误时,所执行的代码块。

**finally** 语句在 trycatch之后无论有无异常都会执行。

注意点: catchfinally语句都是可选的,但在使用 try语句时必须至少使用一个。当错误发生时, JavaScript 会停止执行,并生成一个错误信息。可以使用throw语句 来创建自定义消息(抛出异常)

  1. try {
  2. // tryCode - 尝试执行代码块
  3. }
  4. catch(err) {
  5. // catchCode - 捕获错误的代码块
  6. }
  7. finally {
  8. // finallyCode - 无论 try / catch 结果如何都会执行的代码块
  9. }

参考

  1. 掘金程序me