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

目录
  • 简介
  • 1 面向错误编程
    • 1.1 墨菲定律
    • 1.2 先判否
  • 2. js 内置的错误处理
    • 2.1 Error 类
    • 2.2 throw
    • 2.3 try catch
    • 2.4 Promise.catch
  • 3. 错误处理只有一次
  • 总结

简介

首先,这篇文章一定会引起争议,因为对于错误处理从来就没有真正的标准答案,每个人都会有自己的主观意见。 我的理解毕竟也是片面,提出的想法主要是基于个人的经验总结,如果有异议,欢迎交流讨论。 为了能够尽量保持客观,我会将处理思想尽量前置,再围绕处理思想展开。 这样大家就能先思考这个思想是否能够成为前提,以及在这个思想前提下的最优处理方式。

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 对象也可用于用户自定义的异常的基础对象。js还封装了一些内置的标准错误类型。 如语法错误的 SyntaxError,类型错误的 TypeError,以及 ReferenceError、RangeError、URIError、AggregateError、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

js 中还提供了内置类 Promise,promise 的存在不仅仅是解决了 js 回调地狱的问题,而且按照 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 的时候也有提到,就不展开了。

总结

我们先介绍了面向错误编程的概念,然后讲了 js 在处理错误时的一些方法,以及最后提到了错误处理该由谁去做的问题。ok,结束。

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

(0)

相关推荐

  • javascript中异常处理案例(推荐)

    如下所示: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script type="text/javascript"> // cache 缓存 // try-catch-finall

  • JS中异常抛出和处理方法图文详解

    目录 抛出异常 抛出的表达式类型 基本数据类型 对象 类的实例对象 Error 类的实例对象 Error 的子类 处理异常 js中常见的系统异常: 总结 抛出异常 在 js 中,有时候我们需要处理一些异常或错误.比如编写的某个函数所接收的参数要求是 Number 类型的,如果在该函数被调用时传入的是字符串,就需要发出提醒.此时我们可以使用 throw 语句来抛出个异常: // 例 1 function fn(num) { if (typeof num !== 'number') throw '需

  • javascript异常处理实现原理详解

    这篇文章主要介绍了javascript异常处理实现原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.什么是例外处理 当 JavaScript程序在运行中发生了诸如数组索引越界.类型不匹配或者语法错误时,JavaScript解释器就会引发例外处理. ECMAScript定义了六种类型的错误,除此之外,我们可以使用Error对象和throw语句来创建并引发自定义的例外处理信息. 通过运用例外处理技术,我们可以实现用结构化的方式来响应错误事

  • JavaScript中的异常处理

    一.什么是例外处理 当 JavaScript程序在运行中发生了诸如数组索引越界.类型不匹配或者语法错误时,JavaScript解释器就会引发例外处理. ECMAScript定义了六种类型的错误,除此之外,我们可以使用Error对象和throw语句来创建并引发自定义的例外处理信息. 通过运用例外处理技术,我们可以实现用结构化的方式来响应错误事件的发生,让例外处理代码与正常脚本代码科学分离,最终使我们能够集中精力编写完成主要功能的核心程序. 二.使用 try…catch…finally 执行例外处理

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

    目录 简介 1 面向错误编程 1.1 墨菲定律 1.2 先判否 2. js 内置的错误处理 2.1 Error 类 2.2 throw 2.3 try catch 2.4 Promise.catch 3. 错误处理只有一次 总结 简介 首先,这篇文章一定会引起争议,因为对于错误处理从来就没有真正的标准答案,每个人都会有自己的主观意见. 我的理解毕竟也是片面,提出的想法主要是基于个人的经验总结,如果有异议,欢迎交流讨论. 为了能够尽量保持客观,我会将处理思想尽量前置,再围绕处理思想展开. 这样大家

  • 详解JS中的对象字面量

    前言 在 ES6 之前,js中的对象字面量(也称为对象初始化器)是非常基础的.可以定义两种类型的属性: 键值对{name1: value1} 获取器{ get name(){..} }和 设置器{ set name(val){..}}的计算属性值 var myObject = { myString: 'value 1', get myNumber() { return this._myNumber; }, set myNumber(value) { this._myNumber = Number

  • 详解JS中? ?和?. 和||的区别

    目录 1.?? 与 || 的区别 2.?? 和 ?. 的区别 1.?? 与 || 的区别 1)相同点: ?? 和 || 的用法相同,都是前后是值,中间用符号连接,根据前面的值来判断最终是返回前面的值还是后面的值. One ?? Two One || Two 2)不同点: 判断的方法不同: 使用 ?? 时,只有One为 null 或者 undefined 时才会返回 two; 使用 || 时,One会先转化为布尔值判断,为true时返回One , false 返回Two  // ??   unde

  • 详解js中构造流程图的核心技术JsPlumb(2)

    前言:上篇详解js中构造流程图的核心技术JsPlumb介绍了下JsPlumb在浏览器里面画流程图的效果展示,以及简单的JsPlumb代码示例.这篇还是接着来看看各个效果的代码说明. 一.设置连线的样式和颜色效果代码示例 大概的效果如图: 这些效果看着很简单,那么,我们如何用代码去实现它呢.上章我们说过,JsPlumb的连线样式是由点的某些属性决定的,既然如此,我们就通过设置点的样式来动态改变连线的样式即可.来看代码: 首先来看看连线类型的那个select <div id="btn_line

  • 详解js中的几种常用设计模式

    工厂模式 function createPerson(name, age){ var o = new Object(); // 创建一个对象 o.name = name; o.age = age; o.sayName = function(){ console.log(this.name) } return o; // 返回这个对象 } var person1 = createPerson('ccc', 18) var person2 = createPerson('www', 18) 工厂函数

  • 详解js中的原型,原型对象,原型链

    理解原型 我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法.看如下例子: function Person(){ } Person.prototype.name = 'ccc' Person.prototype.age = 18 Person.prototype.sayName = function (){ console.log(this.name); } var person1 = ne

  • 详解JS中的compose函数和pipe函数用法

    compose函数 compose函数可以将需要嵌套执行的函数平铺,嵌套执行就是一个函数的返回值将作为另一个函数的参数.我们考虑一个简单的需求:这个需求很简单,直接一个计算函数就行: const calculate = x => (x + 10) * 10; let res = calculate(10); console.log(res); // 200 但是根据我们之前讲的函数式编程,我们可以将复杂的几个步骤拆成几个简单的可复用的简单步骤,于是我们拆出了一个加法函数和一个乘法函数: cons

  • 详解JS中的reduce fold unfold用法

    fold(reduce) 说说reduce吧, 很喜欢这个函数,节省了不少代码量,而且有一些声明式的雏形了,一些常见的工具函数,flatten,deepCopy,mergeDeep等用reduce实现的很优雅简洁.reduce也称为fold,本质上就是一个折叠数组的过程,把数组中的多个值经过运算变成一个值,每次运算都会有一个函数处理,这个函数就是reduce的核心元素,称之为reducer,reducer函数是个2元函数,返回1个单值,常见的add函数就是reducer const addRed

  • 详解JS中你不知道的各种循环测速

    前言 在测试循环速度之前,我们先来创建一个有 100 万数据的数组: const len = 100 * 10000; const arr = []; for (let i = 0; i < len; i++) { arr.push(Math.floor(Math.random() * len)); } 测试环境为: 1.电脑:iMac(10.13.6): 2.处理器:4.2 GHz Intel Core i7: 3.浏览器:Chrome(89.0.4389.82) 1. for 循环 for

  • 一文详解JS中的事件循环机制

    目录 前言 1.JavaScript是单线程的 2.同步和异步 3.事件循环 前言 我们知道JavaScript 是单线程的编程语言,只能同一时间内做一件事,按顺序来处理事件,但是在遇到异步事件的时候,js线程并没有阻塞,还会继续执行,这又是为什么呢?本文来总结一下js 的事件循环机制. 1.JavaScript是单线程的 JavaScript 是一种单线程的编程语言,只有一个调用栈,决定了它在同一时间只能做一件事.在代码执行的时候,通过将不同函数的执行上下文压入执行栈中来保证代码的有序执行.在

随机推荐