详解JS中异常与错误处理的正确方法

作者:半夏时间:2026-10-27 02:47:01浏览:181

本文深入解析JavaScript中异常与错误处理的核心思想,提出“面向错误编程”理念,强调先判否原则。文章辨析了错误与异常的区别,规范了try-catch、throw及Promise.catch的使用场景,指出滥用try-catch包裹业务逻辑的危害,并给出错误处理应仅执行一次的正确模式,帮助开发者构建更健壮的代码。

简介

本文内容可能引发争议,因为错误处理领域从未存在绝对的标准答案,每位开发者都有各自的主观见解。本文观点基于个人经验总结,难免片面,若有异议欢迎交流。为保持客观,本文将处理思想前置,围绕该思想展开论述,以便读者先评估该前提是否成立,再探讨在此前提下的最优处理方案。

1 面向错误编程

为何提倡“面向错误编程”?首先需思考:你能确保自己编写的每一行代码都绝对正确吗?即便你的代码完美无缺,又能保证调用的第三方方法不出错吗?显然,我们无法保证所有代码行的正确性,也无法确保外部调用必然成功。因此,如何处理错误成为关键。有人可能会反驳:“既然可能出错,为何还要测试?”我们需要明确,当错误暴露时,可能已造成不可估量的损失。测试人员并不知晓代码内部实现,极易遗漏测试点,这些遗留点如同地雷,随时可能引爆。这也是许多公司高度重视单元测试的原因。关于单元测试将另文详述,本文聚焦于面向错误编程。为防止极端情况,我们需要先考虑错误场景,再处理正常逻辑,这种编程思想即为面向错误编程。

1.1 墨菲定律

墨菲定律指出:若存在两种或以上方式执行某事,且其中一种会导致灾难,则必定有人会选择这种方式。如同船只即便足够稳固、翻船概率极低,仍需配备救生衣。同理,即使代码出错概率仅为百万分之一,一旦对使用者发生,影响即为100%,因此必须制定应急策略。

1.2 先判否

在代码出错问题上,我们可以假设极端情况:每行代码都可能出错。因此,编写代码前需预判错误情况,体现在代码中即先编写错误处理逻辑,最后再处理正确逻辑。面向错误编程的核心技巧在于“先判否”。

function doSomeThing (params) {
  if (!params.a) return 
  if (params.b !== true) return
  // ...
  // 开始写正确代码逻辑
}

错误不是异常

需明确区分:异常是代码运行时产生的异常信息,若不处理会导致代码终止运行;而错误是运行过程中产生的非预期结果。例如发起HTTP请求,即使网络故障导致请求失败,也不应影响代码继续运行。对请求而言,结果仅有成功或失败,失败也是一种结果,故为错误。但在执行JSON.parse时,若解析出错且不处理,会因异常导致代码终止,此为异常。

2. js 内置的错误处理

2.1 Error 类

运行时错误产生时,Error对象会被抛出。Error对象也可作为用户自定义异常的基础对象。JavaScript还封装了一些内置的标准错误类型,如语法错误的SyntaxError、类型错误的TypeError,以及ReferenceError、RangeError、URIError、AggregateError、InternalError。

2.2 throw

throw用于抛出一个异常,常与Error结合使用。

2.3 try catch

try-catch用于捕获可能出现的未知异常。前文提到的JSON.parse即为最佳示例:解析字符串时,无法确保字符串一定是JSON格式,因此必须使用try-catch包裹异常,否则代码将终止运行。然而,许多开发者在try-catch使用中常出现滥用现象,例如用try-catch包裹请求错误:

try {
  const res = await request()
  if (res) {
    //...
  }
} catch(err) {
  // ...
  // 请求错误处理
}

我个人强烈反对这种用法。原因如下:如前所述,请求结果是一个值,即使请求失败,返回的也是一个值。在try块中即使获取到成功值,若后续处理res时发生语法错误,同样会抛出异常。此时,你还能确定catch捕获的异常是请求失败还是处理res时报错吗?我曾言:滥用try-catch与随地大小便无异。希望大家停止使用此方法。

2.4 Promise.catch

JavaScript还提供了内置类Promise。Promise的存在不仅解决了回调地狱问题,且按设定,Promise必定返回一个结果:成功或失败。这与前文提到的请求结果非常契合,例如axios请求库便使用Promise返回请求结果。因此,针对上述例子,应改为:

// request 返回一个 promise
request()
.then(res => {
  // 这里处理请求成功
  // ...
  // 如果业务代码可能出错,再使用 try catch
  try {
    // ...
  } catch (err) {
    // ...
  }
})
.catch()

3. 错误处理只有一次

代码运行出错时,我们只处理一次。若能立即确定错误处理方案,则直接处理。若无法处理该错误,则需将详细错误结果包装好,以便调用者自行处理。

此处继续使用请求举例。请求时可能因token过期导致401错误而失败,你不可能在每个接口调用处都判断是否401吧。这种由401导致的失败应在封装请求方法时处理,具体请求无需再处理,因此无需将401错误告知具体请求使用函数,自行处理即可,只需告知其错误发生即可。

但具体请求的业务错误可能无法处理。举常见例子:获取列表数据时,常与后端约定请求成功码。常见写法如下:

request()
  .then(res => {
    // 假设请求成功码 0 为成功
    if (res.code === 0) {
      // ...
    } else if (res.code === 1) {
      // ...
      // 参数错误
    } else {
      // ...
      // 其他错误
    }
  })

同样,极不建议如此使用。原因有二:首先,业务错误也是错误。then中处理的是请求成功结果,如前所述,若无法处理错误,应交由具体使用方处理,此处业务错误应交由具体请求方的catch处理。因此应改为:

request()
  .then(res => {
    // then 里面只会请求成功,并且能够成功拿到数据
    // ...
  })
  .catch(err => {
    // 获取到包装好的错误信息
    if (err.code === 1) {
      // ...
      // 参数错误
    } else {
      // ...
      // 其他错误
    }
  })

如此可确保整个错误传递路径的正确性,避免处理不该由你处理的错误。具体在封装axios时亦有提及,此处不再展开。

总结

本文介绍了面向错误编程的概念,讲解了JavaScript处理错误时的一些方法,并最后探讨了错误处理应由谁执行的问题。至此结束。

到此这篇关于详解JS中异常与错误处理的正确方法的文章就介绍到这了,更多相关JS异常处理内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

作者最新文章

相关标签: