错误冒泡和拦截


¥Error propagation and interception

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

¥Node.js supports several mechanisms for propagating and handling errors that occur while an application is running. How these errors are reported and handled depends entirely on the type of Error and the style of the API that is called.

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

¥All JavaScript errors are handled as exceptions that immediately generate and throw an error using the standard JavaScript throw mechanism. These are handled using the try…catch construct provided by the JavaScript language.

// 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 机制的任何使用都会引发必须处理的异常,否则 Node.js 进程将立即退出。

¥Any use of the JavaScript throw mechanism will raise an exception that must be handled or the Node.js process will exit immediately.

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

¥With few exceptions, Synchronous APIs (any blocking method that does not return a <Promise> nor accept a callback function, such as fs.readFileSync), will use throw to report errors.

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

¥Errors that occur within Asynchronous APIs may be reported in multiple ways:

  • 某些异步方法返回 <Promise>,你应该始终考虑到它可能会被拒绝。请参阅 --unhandled-rejections 标志,了解进程如何对未处理的 promise 拒绝做出反应。

    ¥Some asynchronous methods returns a <Promise>, you should always take into account that it might be rejected. See --unhandled-rejections flag for how the process will react to an unhandled promise rejection.

    const fs = require('node:fs/promises');
    
    (async () => {
      let data;
      try {
        data = await fs.readFile('a file that does not exist');
      } catch (err) {
        console.error('There was an error reading the file!', err);
        return;
      }
      // Otherwise handle the data
    })(); 
  • 大多数接受 callback 函数的异步方法将接受作为第一个参数传给该函数的 Error 对象。如果第一个参数不是 null 并且是 Error 的实例,则发生了应该处理的错误。

    ¥Most asynchronous methods that accept a callback function will accept an Error object passed as the first argument to that function. If that first argument is not null and is an instance of Error, then an error occurred that should be handled.

    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' 事件。

    ¥When an asynchronous method is called on an object that is an EventEmitter, errors can be routed to that object's 'error' event.

    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 处理的异常。没有此类方法的完整列表;请参考每种方法的文档以确定所需的适当错误处理机制。

    ¥A handful of typically asynchronous methods in the Node.js API may still use the throw mechanism to raise exceptions that must be handled using try…catch. There is no comprehensive list of such methods; please refer to the documentation of each method to determine the appropriate error handling mechanism required.

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

¥The use of the 'error' event mechanism is most common for stream-based and event emitter-based APIs, which themselves represent a series of asynchronous operations over time (as opposed to a single operation that may pass or fail).

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

¥For all EventEmitter objects, if an 'error' event handler is not provided, the error will be thrown, causing the Node.js process to report an uncaught exception and crash unless either: a handler has been registered for the 'uncaughtException' event, or the deprecated node:domain module is used.

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 拦截,因为它们是在调用代码已经退出后抛出的。

¥Errors generated in this way cannot be intercepted using try…catch as they are thrown after the calling code has already exited.

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

¥Developers must refer to the documentation for each method to determine exactly how errors raised by those methods are propagated.