Node.js v20.3.1 文档


错误冒泡和拦截#

Node.js 支持多种机制来传播和处理应用运行时发生的错误。 如何报告和处理这些错误完全取决于 Error 的类型和调用的 API 的风格。

所有 JavaScript 错误都作为异常处理,使用标准 JavaScript throw 机制立即生成并抛出错误。 这些是使用 JavaScript 语言提供的 try…catch 构造 来处理的。

// Throws with a ReferenceError because z is not defined.
try {
  const m = 1;
  const n = m + z;
} catch (err) {
  // Handle the error here.
} 

任何对 JavaScript throw 机制的使用都将引发必须使用 try…catch 处理的异常,否则 Node.js 进程将立即退出。

除了少数例外,同步 API(任何不接受 callback 函数的阻塞方法,例如 fs.readFileSync)将使用 throw 来报告错误。

异步 API 中发生的错误可能会以多种方式报告:

  • 大多数接受 callback 函数的异步方法将接受作为第一个参数传给该函数的 Error 对象。 如果第一个参数不是 null 并且是 Error 的实例,则发生了应该处理的错误。

    const fs = require('node:fs');
    fs.readFile('a file that does not exist', (err, data) => {
      if (err) {
        console.error('There was an error reading the file!', err);
        return;
      }
      // Otherwise handle the data
    }); 
  • 当在 EventEmitter 对象上调用异步方法时,错误可以路由到该对象的 'error' 事件。

    const net = require('node:net');
    const connection = net.connect('localhost');
    
    // Adding an 'error' event handler to a stream:
    connection.on('error', (err) => {
      // If the connection is reset by the server, or if it can't
      // connect at all, or on any sort of error encountered by
      // the connection, the error will be sent here.
      console.error(err);
    });
    
    connection.pipe(process.stdout); 
  • Node.js API 中的一些典型的异步方法可能仍然使用 throw 机制来引发必须使用 try…catch 处理的异常。 没有此类方法的完整列表; 请参考每种方法的文档以确定所需的适当错误处理机制。

'error' 事件机制的使用最常见于 基于流基于事件触发器 API,它们本身代表随时间推移的一系列异步操作(与可能通过或失败的单个操作相反)。

对于所有 EventEmitter 对象,如果没有提供 'error' 事件处理程序,将抛出错误,导致 Node.js 进程报告未捕获的异常并崩溃,除非: domain 模块使用得当或已为 'uncaughtException' 事件注册了处理程序。

const EventEmitter = require('node:events');
const ee = new EventEmitter();

setImmediate(() => {
  // This will crash the process because no 'error' event
  // handler has been added.
  ee.emit('error', new Error('This will crash'));
}); 

以这种方式产生的错误无法使用 try…catch 拦截,因为它们是在调用代码已经退出后抛出的。

开发者必须参考每种方法的文档,以确定这些方法引发的错误是如何传播的。

错误优先回调#

Node.js 核心 API 公开的大多数异步方法都遵循称为错误优先回调的惯用模式。 使用这种模式,回调函数作为参数传给方法。 当操作完成或出现错误时,回调函数将使用 Error 对象(如果有)作为第一个参数传入。 如果没有出现错误,则第一个参数将作为 null 传入。

const fs = require('node:fs');

function errorFirstCallback(err, data) {
  if (err) {
    console.error('There was an error', err);
    return;
  }
  console.log(data);
}

fs.readFile('/some/file/that/does-not-exist', errorFirstCallback);
fs.readFile('/some/file/that/does-exist', errorFirstCallback); 

JavaScript try…catch 机制 cannot 用于拦截异步 API 生成的错误。 初学者的一个常见错误是尝试在错误优先的回调中使用 throw

// THIS WILL NOT WORK:
const fs = require('node:fs');

try {
  fs.readFile('/some/file/that/does-not-exist', (err, data) => {
    // Mistaken assumption: throwing here...
    if (err) {
      throw err;
    }
  });
} catch (err) {
  // This will not catch the throw!
  console.error(err);
} 

这不起作用,因为传给 fs.readFile() 的回调函数是异步调用的。 当回调被调用时,周围的代码(包括 try…catch 块)已经退出。 在大多数情况下,在回调 会使 Node.js 进程崩溃 中抛出错误。 如果启用了 ,或者已经向 process.on('uncaughtException') 注册了处理程序,则可以拦截此类错误。