深入了解JavaScript/Node.js中的错误处理

深入了解JavaScript/Node.js中的错误处理

什么是错误?

错误是用户执行的非法操作,导致程序异常工作。

错误类型: 错误可以广泛分类为

  • 操作错误: 在程序执行任务时发生的错误。例如,网络故障就是操作错误。
  • 开发者错误: 这种错误发生在开发者犯了一个错误的情况下。这种错误的主要示例是无效的输入。在这些情况下,程序不应继续运行,而应崩溃并提供有用的描述,以便开发者可以解决他们的错误。

抛出错误: 通常可以使用 throw 关键字来处理输入错误。

示例:

JavaScript

function doTask(amount) { 
    if (typeof amount !== 'number')  
        throw new Error('amount must be a number') 
    return amount / 2 
} 
  
doTask("This will generate an error")

输出:

深入了解JavaScript/Node.js中的错误处理

正如我们所观察到的,当程序运行时,我们得到一个错误信息,错误信息显示“金额必须为数字。”

因此,我们可以限制程序可以接受的输入类型,并停止程序的执行,以避免生成任何意外或错误的输出。

Javascript中的本地错误类: 还有六个继承自基本错误类Error的本地错误构造函数,它们分别是:

  • ReferenceError
  • EvalError
  • SyntaxError
  • RangeError
  • TypeError
  • URIError

这些错误构造函数主要用于原生JavaScript API和功能。

1.引用错误: 当我们使用未定义的变量或尚未定义的变量时,会抛出ReferenceError类型的错误。

语法:

node -p "thisVariableIsNotDefined"

示例:

Javascript

const defined = "This variable is defined"; 
console.log(defined); 
console.log(notDefined);  
// There is no such declaration for  
// a variable named notDefined and  
// thus results in a reference error

输出:

深入了解JavaScript/Node.js中的错误处理

这是因为没有名为 thisVariableIsNotDefined 的变量,因此抛出了一个 ReferenceError 错误。

2. EvalError: EvalError对象指示与全局eval()函数相关的错误。此异常不再由JavaScript抛出。但是,EvalError对象仍然保留以保持兼容性。

由于错误不再由javascript抛出,我们可以手动抛出 EvalError 对象。

语法:

throw new EvalError('message', 
'file_where_error_occurs', 'line_number')

示例: 一个非常常见的 EvalError 的示例是我们试图将一个数字除以0,下面是模拟相同情况的代码片段,注意默认情况下JavaScript引擎不会抛出EvalError,所以我们需要手动抛出错误。先看一下下面的示例,然后我们将讨论代码片段中究竟发生了什么。

JavaScript

try { 
    const a = 7; 
    const b = 0; 
    if (b === 0)  
        throw new EvalError("Cannot divide a number by 0"); 
    let quot = eval(a / b); 
    console.log(`Quotient = ${quot}`); 
} catch (err) { 
    console.log("Error is of Type : " + err.name); 
    console.log("Error Message : " + err.message); 
}

输出结果:

深入了解JavaScript/Node.js中的错误处理

我们声明了两个变量,分别是 ab ,并用一些数字初始化它们。然后我们检查变量b是否等于0,如果是的话,抛出一个新的 EvalError ,然后在try/catch块中处理它,我们在这里打印错误类型和错误信息。

3. SyntaxError: eval()函数将表示为字符串的JavaScript代码评估并返回其完成值。

为了理解 eval() 的用法,看下面的示例:

深入了解JavaScript/Node.js中的错误处理

正如你可以看到的 操作符 ,并且 两个操作数 ,是 eval() 函数所必需的。

现在让我们看一下,如果你在 eval() 函数中不传递操作符时会发生什么:

深入了解JavaScript/Node.js中的错误处理

我们可以观察到SyntaxError的发生,因为eval()函数需要一个操作符和两个操作数,所以它会抛出一个错误;因为找不到操作数。

4. RangeError: 当遇到越界(超出界限)的值时,会抛出RangeError错误。例如,在银行应用程序中,账户余额不能为负数。

Javascript

try { 
    const a = 7; 
    const b = 0; 
    if (b === 0)  
        throw new EvalError("Cannot divide a number by 0"); 
    let quot = eval(a / b); 
    console.log(`Quotient = ${quot}`); 
} catch (err) { 
    console.log("Error is of Type : " + err.name); 
    console.log("Error Message : " + err.message); 
}

输出:

深入了解JavaScript/Node.js中的错误处理

上面的代码块非常容易理解,它的作用是生成一个随机的三位数,但是请注意if条件。我们已经指定只允许0到99之间的值,由于random()函数生成的是三位数,所以会出现范围错误。

5. TypeError: TypeError对象表示在执行操作时发生错误,通常(但不限于)是因为值的类型不符合预期。考虑以下示例,我们编写一个函数来添加两个数字,并期望传递给函数的参数都是数字类型,如果不是,则抛出TypeError。

Javascript

const num1 = 2; 
const num2 = "This is a string"; 
const add = (var1, var2) => { 
    try { 
        if (typeof var1 !== "number" 
        || typeof var2 !== "number") 
            throw new TypeError( 
                "Only numeric values are allowed"); 
        return var1 + var2; 
    } catch (err) { 
        console.log(err.message); 
    } 
}; 
add(num1, num2);

输出:

深入了解JavaScript/Node.js中的错误处理

执行上面的代码块将导致 TypeError ,因为传递的参数之一是字符串类型,显然不是一个数字。

6. URIError: 如果URI的编码或解码失败,将出现URIError。这个错误是因为在代码中的某个地方URI的编码或解码失败。

考虑以下代码片段中的代码行:

首先是有效的URI编码示例:

深入了解JavaScript/Node.js中的错误处理

以下是另一个示例,我们传递了一个无效的编码字符串,导致了URIError错误,输出字符串 ‘URI Malformed’

深入了解JavaScript/Node.js中的错误处理

我们还可以验证错误对象,并检查错误对象是否是特定类的实例。

考虑下面的示例:

深入了解JavaScript/Node.js中的错误处理

首先,我们检查错误是否是 Error 类的一个实例,返回true,然后我们检查 SyntaxError 是否是 Error 类的一个实例,返回true,因为 SyntaxError 是从 Error 类继承的。最后,我们检查 SyntaxError 是否是 ReferenceError 类的一个实例,返回false,因为 SyntaxError 是从 Error 类继承的,而不是 ReferenceError 类。

自定义错误: 让我们更新 doTask 函数,当输入的数字是奇数时抛出自定义错误,这是 doTask 函数的实现

Javascript

function doTask(amount) { 
    if (typeof amount !== 'number')  
        throw new TypeError('amount must be a number') 
    if (amount <= 0)  
        throw new RangeError( 
            'amount must be greater than zero') 
    if (amount % 2) { 
        const err = Error('amount must be even') 
        err.code = 'ERR_NUM_MUST_BE_EVEN'
        throw err 
    } 
    return amount / 2 
} 
  
doTask(3)

运行上述代码片段后,我们在输出窗口中得到的结果如下:
深入了解JavaScript/Node.js中的错误处理

这是一种定义自定义错误的方法,另一种方法是通过继承 Error 类来创建自定义错误。

下面是一个自定义错误类的实现。

Javascript

class OddError extends Error { 
  constructor (varName = '') { 
    super(`${varName} must be even`) 
  } 
  get name () { return 'OddError' } 
} 
  
function doTask (amount) { 
  if (typeof amount !== 'number')  
      throw new TypeError('amount must be a number') 
  if (amount <= 0)  
      throw new RangeError('amount must be greater than zero') 
  if (amount % 2) throw new OddError('amount') 
  return amount / 2 
} 
  
doTask(3)

在执行上述代码片段后,在输出窗口中会得到类似于以下的输出内容:

深入了解JavaScript/Node.js中的错误处理

我们可以在自定义错误类中添加 code 属性;我们可以对 OddError 类进行更改,做如下操作:

Javascript

class OddError extends Error { 
    constructor(varName = '') { 
        super(varName + ' must be even') 
        this.code = 'ERR_MUST_BE_EVEN'
    } 
    get name() { 
        return 'OddError [' + this.code + ']'
    } 
}

当我们使用上述更新的自定义错误类时,在错误发生时也会在输出中获得错误代码 –

深入了解JavaScript/Node.js中的错误处理

使用Try/Catch块进行错误处理:

当在普通同步函数中抛出错误时,可以使用try/catch块进行处理。

使用前面章节中的相同代码,我们将对doTask(3)函数调用进行try/catch块包装:

Javascript

try { 
    const result = doTask(3) 
    console.log('Result : ', result) 
} catch (err) { 
    console.error('Error caught : ', err) 
}

执行这个更新的代码将导致以下结果:

深入了解JavaScript/Node.js中的错误处理

在这种情况下,我们控制了错误输出到终端,但是使用这种模式,我们也可以根据情况应用任何错误处理措施。

让我们将传递给doTask的参数更新为有效输入:

Javascript

try { 
    const result = doTask(4) 
    console.log('result', result) 
} catch (err) { 
    console.error('Error caught: ', err) 
}

这将导致以下输出:

深入了解JavaScript/Node.js中的错误处理

拒绝: 在同步上下文中的 抛出异常 被称为异常。当一个 promise 被拒绝时,它表示一个 异步错误 。可以将异常和拒绝看作是同步错误和异步错误的两种思考方式。

假设 doTask 有一些异步工作要做,我们可以使用基于回调的 API 或者使用基于 promise 的 API(即使是基于 async/await 的也是基于 promise 的)。

让我们将 doTask 转换为返回一个解析为值的 promise,如果有错误则拒绝:

Javascript

function doTask(amount) { 
    return new Promise((resolve, reject) => { 
        if (typeof amount !== 'number') { 
            reject(new TypeError('amount must be a number')) 
            return
        } 
        if (amount <= 0) { 
            reject(new RangeError('amount must be greater than zero')) 
            return
        } 
        if (amount % 2) { 
            reject(new OddError('amount')) 
            return
        } 
        resolve(amount / 2) 
    }) 
} 
  
doTask(3)

但是等等,拒绝被搁置了,因为Promises必须使用try / catch方法来捕获拒绝,而到目前为止我们还没有附加一个catch处理程序。让我们将doTask调用修改为以下内容:

Javascript

doTask(3) 
    .then((result) => { 
        console.log('result', result) 
    }) 
    .catch((err) => { 
        if (err.code === 'ERR_AMOUNT_MUST_BE_NUMBER') { 
            console.error('wrong type') 
        } else if (err.code === 'ERRO_AMOUNT_MUST_EXCEED_ZERO') { 
            console.error('out of range') 
        } else if (err.code === 'ERR_MUST_BE_EVEN') { 
            console.error('cannot be odd') 
        } else { 
            console.error('Unknown error', err) 
        } 
  
    })

输出:

深入了解JavaScript/Node.js中的错误处理

当我们使用有效的值调用 doTask 函数时,它会解析并打印结果。

异步的try/catch: async/await语法支持对拒绝的try/catch。换句话说,我们可以在异步基于promise的API上使用try/catch,而不是像在下一节中使用then和catch处理程序,让我们创建一个名为run的异步函数,并重新引入与调用doTask同步形式时使用的相同的try/catch模式:

Javascript

async function run() { 
    try { 
        const result = await doTask(3) 
        console.log('result', result) 
    } catch (err) { 
        if (err instanceof TypeError) { 
            console.error('wrong type') 
        } else if (err instanceof RangeError) { 
            console.error('out of range') 
        } else if (err.code === 'ERR_MUST_BE_EVEN') { 
            console.error('cannot be odd') 
        } else { 
            console.error('Unknown error', err) 
        } 
    } 
} 
  
run()

唯一的区别是,除了将try/catch包装在一个异步函数中,我们还使用await关键字来等待doTask(3)的结果,这样异步函数就可以自动处理Promise了。由于3是一个奇数,doTask返回的Promise会调用reject方法,并传递我们自定义的OddError对象,catch块会识别code属性,然后输出 cannot be odd

深入了解JavaScript/Node.js中的错误处理

这个函数的工作方式与基于promise的方法相同,唯一的区别是它是通过异步地使用 Try/Catch 块来实现的。

始终将错误处理好放在代码的最顶层,例如-在NodeJS中app.js是应用程序的入口点,所有错误都应该在这里处理。

错误可以通过重新抛出错误传递到最顶层,考虑下面的代码片段

Javascript

const parent_function = () => { 
    try { 
        child_function() 
    } catch (err) { 
        console.log("All Errors are handled"
            + "here in the parent function"); 
        console.log(`Type of Error :  
            {err.name}\nError Message :{err.message}`) 
    } 
} 
const child_function = () => { 
    try { 
        throw new Error("Error From Child Function") 
    } catch (err) { 
        throw new Error(err.message); 
    } 
} 
parent_function()

请将 parent_function 视为 程序的入口点,也请注意我们在 parent_function 中处理了child_function 中发生的错误。

也许这有点令人困惑,但是当你看到输出结果时,你会明白上面的代码片段的作用。

深入了解JavaScript/Node.js中的错误处理

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程