错误的传播和拦截
Node.js 支持多种机制来传播和处理应用程序运行时发生的错误。
如何报告和处理这些错误完全取决于 Error
的类型和调用的 API 的风格。
所有的 JavaScript 错误都作为异常处理,使用标准的 JavaScript throw
机制立即生成并抛出错误。
这些是使用 JavaScript 语言提供的 try…catch
构造处理的。
// 由于 z 未定义,因此抛出 ReferenceError。
try {
const m = 1;
const n = m + z;
} catch (err) {
// 在此处理错误。
}
任何使用 JavaScript throw
机制都会引发异常,必须使用 try…catch
处理,否则 Node.js 进程将立即退出。
除了少数例外,同步的 API(任何不接受 callback
函数的阻塞方法,例如 fs.readFileSync
)都使用 throw
来报告错误。
异步的 API 中发生的错误可以以多种方式报告:
- 大多数接受
callback
函数的异步方法将接受作为第一个参数传给该函数的Error
对象。 如果第一个参数不是null
并且是Error
的实例,则发生了应该处理的错误。
const fs = require('fs');
fs.readFile('a file that does not exist', (err, data) => {
if (err) {
console.error('There was an error reading the file!', err);
return;
}
// 否则处理数据
});
-
当在
EventEmitter
对象上调用异步方法时,错误可以路由到该对象的'error'
事件。const net = require('net'); const connection = net.connect('localhost'); // 向流中添加 'error' 事件句柄: connection.on('error', (err) => { // 如果连接被服务器重置, // 或者根本无法连接,或者连接遇到任何类型的错误, // 则错误将发送到这里。 console.error(err); }); connection.pipe(process.stdout);
-
Node.js API 中的一些典型的异步方法可能仍然使用
throw
机制来引发必须使用try…catch
处理的异常。 没有此类方法的完整列表;请参阅每种方法的文档以确定所需的适当错误处理机制。
'error'
事件机制的使用最常见于基于流和基于事件触发器的 API,其本身代表了一系列随时间推移的异步操作(而不是单个操作可能通过或失败)。
对于所有的 EventEmitter
对象,如果未提供 'error'
事件句柄,则将抛出错误,导致 Node.js 进程报告未捕获的异常并崩溃,除非:domain
模块使用得当或已为 'uncaughtException'
事件注册句柄。
const EventEmitter = require('events');
const ee = new EventEmitter();
setImmediate(() => {
// 这将导致进程崩溃,
// 因为没有添加 'error' 事件句柄。
ee.emit('error', new Error('This will crash'));
});
以这种方式产生的错误不能使用 try…catch
拦截,因为其抛出后调用代码已经退出。
开发者必须参考每种方法的文档,以确定这些方法引发的错误是如何传播的。
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.
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.
}
Any use of the JavaScript throw
mechanism will raise an exception that
must be handled using try…catch
or the Node.js process will exit
immediately.
With few exceptions, Synchronous APIs (any blocking method that does not
accept a callback
function, such as fs.readFileSync
), will use throw
to report errors.
Errors that occur within Asynchronous APIs may be reported in multiple ways:
- Most asynchronous methods that accept a
callback
function will accept anError
object passed as the first argument to that function. If that first argument is notnull
and is an instance ofError
, then an error occurred that should be handled.
const fs = require('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
});
-
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('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);
-
A handful of typically asynchronous methods in the Node.js API may still use the
throw
mechanism to raise exceptions that must be handled usingtry…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.
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).
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: The domain
module is
used appropriately or a handler has been registered for the
'uncaughtException'
event.
const EventEmitter = require('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'));
});
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.