1、使用throw关键字抛出异常

throw关键字用于抛出一个异常,它可以在特定的情形下自行抛出异常。throw语句的基本格式如下:

throw value

参数value表示抛出的异常,它的值可以是任何JavaScript类型的值(包括字符串、数字或对象等)​。例如,在JavaScript代码中使用下面代码抛出不同类型的异常都是合法的:

throw "程序出错了";  //抛出了一个值为字符串的异常
throw 1;             //抛出了一个值为整数1的异常
throw true;          //抛出了一个值为true的异常

但在Node.js中,通常不抛出这些类型的值,而是抛出Error对象,例如下面的代码:

throw new Error('程序出错了')

2、Error错误对象

Error对象是一个错误对象,它由Error核心模块提供,当使用Error对象时,并不表明错误发生的具体情况,它会捕获堆栈跟踪,并提供所发生错误的描述内容。Error对象的使用方法如下:

new Error(message)

参数message表示要显示的错误信息。

Error对象提供了一些属性,用于获取错误相关的信息,分别如下。

  • name属性:获取错误的类型名称,比如内置错误类型TypeError等。
  • message属性:获取错误信息。
  • stack属性:获取代码中Error被实例化的位置。

Error类是Node.js中所有错误类的基类,其常用子类及说明如表所示。

Error类的子类 说明
AssertionError 断言错误
RangeError 表明提供的参数不在函数的可接受值的集合或范围内,无论是数字范围,还是给定的函数参数选项的集合
ReferenceError 表明试图访问一个未定义的变量,此类错误通常表明代码有拼写错误或程序已损坏
SyntaxError 表明程序不是有效的JavaScript,.这些错误可能仅在代码评估的结果中产生和传播
SystemError 表明Nod©.js在运行时环境中发生异常时会生成系统错误,这通常发生在应用程序违反操作系统约束时,例如,如果应用程序试图读取不存在的文件,则会发生系统错误

例如,下面代码定义了一个代码块,其中通过实例化Error对象创建了一个异常,并使用throw关键字抛出该异常:

let syncError = ()=>{
	throw new Error('自定义异常');
}

3、使用try…catch语句捕获异常

异常定义之后,需要在程序中捕获,这时需要使用try…catch语句。try…catch语句允许在try后面的大括号{}中放置可能发生异常情况的程序代码,以对这些代码进行监控;在catch后面的大括号{}中放置处理异常的程序代码。try…catch语句的基本语法如下:

try {
               //可能会出错的代码,出错时抛出一个错误
} catch (e) {
               //处理异常的代码
}

参数e表示捕获的异常。

例如,下面代码使用try…catch捕获17.3.2节示例代码中抛出的异常信息:

try {
     syncError()
} catch (e) {
     console.log(e.message)
}
console.log('异常被捕获')

程序运行如下:

自定义异常
异常被捕获

在开发程序时,如果遇到需要处理多种异常信息的情况,可以在一个try代码块后面跟多个catch代码块,这里需要注意的是,如果使用了多个catch代码块,则catch代码块中的异常类顺序是先子类后父类。

完整的异常处理语句应该包含finally代码块,通常情况下,无论程序中有无异常产生,finally代码块中的代码都会被执行,其语法格式如下:

try{
            //可能会出错的代码,出错时抛出一个错误
}catch(e){
            //处理异常的代码
}finally{
            //最终执行的代码
}

使用try…catch…finally语句时,不管try代码块内有没有抛出异常,finally代码块总会被执行。如果try代码块内发生错误,finally代码块将在catch代码块之后被执行;如果没有发生错误,将跳过catch代码块,直接执行finally代码块中的代码。

使用异常处理语句时,可以不写catch代码块,比如写成try…finally的形式,但需要注意的是,try代码块后必须至少跟一个catch或finally代码块,不能只写try。

例如,使用同步方式读取一个文件,并使用try…catch语句捕获文件不存在错误,最后在finally代码块内输出“执行完毕”的提示,代码如下:

const fs=require("fs")
try{
     var data = fs.readFileSync("test.txt", {"encoding":"utf8"})
} catch (err) {
     console.log("文件不存在")
     throw err;
} finally {
     console.log("执行完毕!")
}

运行结果如下:

文件不存在
执行完毕!
ENOENT: no such file or directory, open 'test.txt'

4、异步程序中的异常处理

如果是异步程序出现异常,该如何捕获呢?例如,下面是一个异步代码块,其中抛出了一个异常,代码如下:

//模拟异步代码块内出现异常
let asyncError = () => {
     setTimeout(function () {
          throw new Error('异步异常')
     }, 100)
}

如果我们使用传统的try…catch捕获上面异步代码中抛出的异常,则写法应该如下:

(async function () {
     try {
          await asyncError()
     } catch (e) {
          console.log(e.message)  //处理异常
     }
})()

但在运行程序时,却出现了如图所示的结果。
在这里插入图片描述

通过上面示例代码可以看出,异步代码中的异常是无法使用try…catch方法捕获的,那么,如何捕获异步程序中的异常呢?Node.js中提供了两种方法,用于捕获异步程序中的异常,分别如下。

  • process方式。process模块是Node.js提供给开发者用来和当前进程进行交互的工具,通过监听它的uncaughtException事件,可以处理所有未被捕获的异常,包括同步代码块中的异常和异步代码块中的异常。例如,下面代码用来捕获本节开始定义的异步代码中的异常:
process.on('uncaughtException', function (e) {
     console.log(e.message)                     //处理异常
});
asyncError()
  • domain方式。通过监听domain模块的error事件来处理异步代码块中的异常,domain模块主要用来简化异步代码的异常处理,它可以处理try…catch无法捕捉的异常。例如,下面代码使用domain方式捕获本节前面定义的异步代码中的异常:
let domain = require('domain')
let d = domain.create()
d.on('error', function (e) {
     console.log(e.message)     //处理异常
})
d.run(asyncError)

使用process方式和domain方式都可以捕获异步代码块中的异常,但process方式只适用于记录异常信息的场合,因此,在捕获异步代码库中的异常时,推荐使用domain方式。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐