try...catch

Daotin 于 2022-01-28 发布 编辑
  1. What是什么
  2. Why为什么使用?
    1. 基本语法
  3. How使用条件
  4. 注意事项
  5. 参考文档

What是什么

try...catch语句标记要尝试的语句块,并指定一个出现异常时抛出的响应。

Why为什么使用?

程序要健壮,必须要设计报错机制。try {…} 块内的错误不会杀死脚本 — 我们有机会在 catch 中处理它。

基本语法

try {
    //可能会导致错误的代码
} catch (error) {
    //在错误发生时怎么处理
}finally {
     //即使报错始终执行
 }

即使try..catch中有return,finally也会在返回前执行。

Error对象

发生错误时,JavaScript 生成一个包含有关其详细信息的对象。然后将该对象作为参数传递给 catch

我们将 catch 块仅仅用于显示信息,但是我们可以做更多的事儿:发送一个新的网络请求,向访问者建议一个替代方案,将有关错误的信息发送给记录日志的设备,……。所有这些都比代码“死掉”好得多。

对于所有内建的 error,error 对象具有两个主要属性:

name

Error 名称。例如,对于一个未定义的变量,名称是 "ReferenceError"

message

关于 error 的详细文字描述。

还有其他非标准的属性在大多数环境中可用。其中被最广泛使用和支持的是:

stack

当前的调用栈:用于调试目的的一个字符串,其中包含有关导致 error 的嵌套调用序列的信息。

try {
  lalala; // error, variable is not defined!
} catch(err) {
  alert(err.name); // ReferenceError
  alert(err.message); // lalala is not defined
  alert(err.stack); // ReferenceError: lalala is not defined at (...call stack)

  // 也可以将一个 error 作为整体显示出来as a whole
  // Error 信息被转换为像 "name: message" 这样的字符串
  alert(err); // ReferenceError: lalala is not defined
}

How使用条件

只有错误可预知时才用try。所有不可预知的错误用try都是不负责任的写法。

try catch的使用,永远应该放在你的控制范围之内,而不应该防范未知的错误。也就是说你很清楚知道这里是有可能”出错“的,而且你很清楚知道什么前提下会出错,你就是要故意利用报错信息来区分错误,后续的程序会解决所有的出错,让程序继续执行。

如果让用户先发现你根本没预料到的错误,而不是你先发现错误,你是失职的。

大多数情况下,try catch适用于两种场合:

1、浏览器原罪的场合:也就是兼容性场合,因为浏览器兼容性不是程序员能改正的,所以只能try catch:由于不同浏览器的报错提示是不一样的,根据捕获的浏览器的报错提示判断用户的浏览器,然后做出对应的措施,这时候使用try catch是巧妙的办法,如果用if就比较笨拙,因为if通常只能反馈真或假,不能直接反馈浏览器的报错内容。

2、考虑如下代码。window.a.b是非法的,再跟2对比就没有意义,这样非法的条件,在try catch中仍可以继续运行下去。但在if中window.a.b已经报错,整个页面都会坏掉。如果希望用if写,那么必须先判断window.a是否是合法的,window.a是合法的前提下再判断window.a.b是不是合法的,如果也是合法的,再判断window.a.b是否不等于2,这样是不是很蠢?这时就体现出try catch的妙处了,程序不但知道window.a.b !== 2是假的,而且直接可以知道究竟哪一步就已经是假的。

再想象一下,有一个变量是json.a.b.c,其中的a/b/c都可能是存在的也可能是不存在的,全看具体情况,这时候你简单的写if (json.a.b.c === 2) {…}是不行的,因为json.a.b就可能已经是非法的,所以你如果用if,就要考虑a是不是非法的、a是合法前提下b是不是非法的,b是合法前提下c是不是非法的。但是json.a.b.c === 2在try中就可以直接写,也就是说,我不关心究竟a/b/c谁是非法的,我只关心json.a.b.c到底是不是等于2,不等于2或者任何一步出错,对我来讲没有区别,反正都是不等于2,我不关心哪步出错,而且程序不会坏掉。这是一种比较省心的写法。

另外注意,try catch不能做真假判断,只能做非法判断。也就是说:try {1 === 2},虽然1===2是假,但是是合法的,catch不会捕捉到错误,也不会告诉你1 === 2到底是真是假。所以,写在try里的应该是json.a.b.c而不是json.a.b.c === 2。究竟是不是等于2,是后面的事,是if干的事。简单说,try catch用于捕捉报错,当你不关心哪一步错误,只关心有没有错,就用try catch。

前端可以用在上传图片、使用别人的js库报错、async await同步调接口等地方适用。

JSON.parse,如果 **json** 格式错误,JSON.parse** 就会生成一个 error,因此脚本就会“死亡”。**

对应数据类型的错误,建议小伙伴们用解构赋值指定默认值、&&||来规避,所以慎用try catch。

经典请求接口的场景

async getAjax() {
  try {
    let response = await fetch(url);
    let data = await response.json(); 
    console.log(data);
  } catch(e) {
    console.error("Oops, error", e);
  }
}

await后面可以跟 Promise 对象,表示等待 Promiseresolve()才会继续向下执行,如果 Promise 被reject()或抛出异常则会被外面的try...catch捕获。

注意事项

try catch捕获不到异步错误

尝试对异步方法进行try catch操作只能捕获当次事件循环内的异常,对callback执行时抛出的异常将无能为力。

try {
    setTimeout(()=>{
        const A = 1
        A = 2
    },0)
} catch (err) {
    // 这里并不能捕获回调里面抛出的异常
    console.log("-----catch error------")
    console.log(err)
}

异步情况想捕获异常,建议在异步函数里包一层try catch

setTimeout(() => {
  try {
    const A = 1
    A = 2
  } catch (err) {
    console.log(err)
  }
}, 0)

2、要使得 try..catch 能工作,代码必须是可执行的。换句话说,它必须是有效的 JavaScript 代码。

如果代码包含语法错误,那么 try..catch 将无法正常工作,例如含有...

try {
  ...
} catch(e) {
  alert("The engine can't understand this code, it's invalid");
}

所以,try..catch 只能处理有效代码中出现的错误。这类错误被称为“运行时的错误(runtime errors)”,有时被称为“异常(exceptions)”。

几条建议:

参考文档

错误处理,”try…catch”

(完)