异常处理
1、使用throw关键字抛出异常
throw关键字用于抛出一个异常,它可以在特定的情形下自行抛出异常。throw语句的基本格式如下:
js
throw value
参数value表示抛出的异常,它的值可以是任何JavaScript类型的值(包括字符串、数字或对象等)。例如,在JavaScript代码中使用下面代码抛出不同类型的异常都是合法的:
js
throw "程序出错了"; //抛出了一个值为字符串的异常
throw 1; //抛出了一个值为整数1的异常
throw true; //抛出了一个值为true的异常
但在Node.js中,通常不抛出这些类型的值,而是抛出Error对象,例如下面的代码:
js
throw new Error('程序出错了')
2、Error错误对象
Error对象是一个错误对象,它由Error核心模块提供,当使用Error对象时,并不表明错误发生的具体情况,它会捕获堆栈跟踪,并提供所发生错误的描述内容。Error对象的使用方法如下:
js
new Error(message)
参数message表示要显示的错误信息。
Error对象提供了一些属性,用于获取错误相关的信息,分别如下。
name属性:获取错误的类型名称,比如内置错误类型TypeError等。message属性:获取错误信息。stack属性:获取代码中Error被实例化的位置。
Error类是Node.js中所有错误类的基类,其常用子类及说明如表所示。
| Error类的子类 | 说明 |
|---|---|
AssertionError |
断言错误 |
RangeError |
表明提供的参数不在函数的可接受值的集合或范围内,无论是数字范围,还是给定的函数参数选项的集合 |
ReferenceError |
表明试图访问一个未定义的变量,此类错误通常表明代码有拼写错误或程序已损坏 |
SyntaxError |
表明程序不是有效的JavaScript,.这些错误可能仅在代码评估的结果中产生和传播 |
SystemError |
表明Nod©.js在运行时环境中发生异常时会生成系统错误,这通常发生在应用程序违反操作系统约束时,例如,如果应用程序试图读取不存在的文件,则会发生系统错误 |
例如,下面代码定义了一个代码块,其中通过实例化Error对象创建了一个异常,并使用throw关键字抛出该异常:
js
let syncError = ()=>{
throw new Error('自定义异常');
}
3、使用try...catch语句捕获异常
异常定义之后,需要在程序中捕获,这时需要使用try...catch语句。try...catch语句允许在try后面的大括号{}中放置可能发生异常情况的程序代码,以对这些代码进行监控;在catch后面的大括号{}中放置处理异常的程序代码。try...catch语句的基本语法如下:
js
try {
//可能会出错的代码,出错时抛出一个错误
} catch (e) {
//处理异常的代码
}
参数e表示捕获的异常。
例如,下面代码使用try...catch捕获17.3.2节示例代码中抛出的异常信息:
js
try {
syncError()
} catch (e) {
console.log(e.message)
}
console.log('异常被捕获')
程序运行如下:
自定义异常
异常被捕获
在开发程序时,如果遇到需要处理多种异常信息的情况,可以在一个try代码块后面跟多个catch代码块,这里需要注意的是,如果使用了多个catch代码块,则catch代码块中的异常类顺序是先子类后父类。
完整的异常处理语句应该包含finally代码块,通常情况下,无论程序中有无异常产生,finally代码块中的代码都会被执行,其语法格式如下:
js
try{
//可能会出错的代码,出错时抛出一个错误
}catch(e){
//处理异常的代码
}finally{
//最终执行的代码
}
使用try...catch...finally语句时,不管try代码块内有没有抛出异常,finally代码块总会被执行。如果try代码块内发生错误,finally代码块将在catch代码块之后被执行;如果没有发生错误,将跳过catch代码块,直接执行finally代码块中的代码。
使用异常处理语句时,可以不写catch代码块,比如写成try...finally的形式,但需要注意的是,try代码块后必须至少跟一个catch或finally代码块,不能只写try。
例如,使用同步方式读取一个文件,并使用try...catch语句捕获文件不存在错误,最后在finally代码块内输出"执行完毕"的提示,代码如下:
js
const fs=require("fs")
try{
var data = fs.readFileSync("test.txt", {"encoding":"utf8"})
} catch (err) {
console.log("文件不存在")
throw err;
} finally {
console.log("执行完毕!")
}
运行结果如下:
js
文件不存在
执行完毕!
ENOENT: no such file or directory, open 'test.txt'
4、异步程序中的异常处理
如果是异步程序出现异常,该如何捕获呢?例如,下面是一个异步代码块,其中抛出了一个异常,代码如下:
js
//模拟异步代码块内出现异常
let asyncError = () => {
setTimeout(function () {
throw new Error('异步异常')
}, 100)
}
如果我们使用传统的try...catch捕获上面异步代码中抛出的异常,则写法应该如下:
js
(async function () {
try {
await asyncError()
} catch (e) {
console.log(e.message) //处理异常
}
})()
但在运行程序时,却出现了如图所示的结果。

通过上面示例代码可以看出,异步代码中的异常是无法使用try...catch方法捕获的,那么,如何捕获异步程序中的异常呢?Node.js中提供了两种方法,用于捕获异步程序中的异常,分别如下。
process方式。process模块是Node.js提供给开发者用来和当前进程进行交互的工具,通过监听它的uncaughtException事件,可以处理所有未被捕获的异常,包括同步代码块中的异常和异步代码块中的异常。例如,下面代码用来捕获本节开始定义的异步代码中的异常:
js
process.on('uncaughtException', function (e) {
console.log(e.message) //处理异常
});
asyncError()
domain方式。通过监听domain模块的error事件来处理异步代码块中的异常,domain模块主要用来简化异步代码的异常处理,它可以处理try...catch无法捕捉的异常。例如,下面代码使用domain方式捕获本节前面定义的异步代码中的异常:
js
let domain = require('domain')
let d = domain.create()
d.on('error', function (e) {
console.log(e.message) //处理异常
})
d.run(asyncError)
使用process方式和domain方式都可以捕获异步代码块中的异常,但process方式只适用于记录异常信息的场合,因此,在捕获异步代码库中的异常时,推荐使用domain方式。