Node.js作为单进程运行的JavaScript运行时,一旦出现未捕获的异常,默认会直接终止进程,这对于需要长期运行的服务类应用来说是非常危险的。因此掌握未捕获异常的优雅处理方法,是Node.js开发者必备的技能。

未捕获异常的核心处理思路
Node.js提供了专门的事件机制来捕获未处理的异常,核心是通过监听process对象的相关事件,在异常发生时执行自定义的处理逻辑,而不是直接让进程退出。
同步异常的捕获:uncaughtException事件
对于同步代码中抛出的未被捕获的异常,可以通过监听process对象的uncaughtException事件来处理。这个事件会在同步异常未被任何try/catch捕获时触发。
下面是一个基础的处理示例:
// 监听未捕获的同步异常
process.on('uncaughtException', (err) => {
// 记录异常信息到日志
console.error('捕获到未处理的同步异常:', err.message);
console.error('异常堆栈:', err.stack);
// 可以在这里执行资源清理操作,比如关闭数据库连接
// 注意:不建议在这个事件回调中继续运行复杂的业务逻辑,避免二次异常
});
// 触发一个未捕获的同步异常
setTimeout(() => {
throw new Error('这是一个未捕获的同步异常');
}, 1000);
需要注意的是,uncaughtException事件触发后,Node.js进程默认还是会退出,如果你希望进程继续运行,需要在回调中手动处理,但这种做法风险较高,因为异常可能导致应用状态不一致,通常建议在处理完日志和清理操作后,主动退出进程,再由守护进程重启。
异步异常的捕获:unhandledRejection事件
在Promise普及之后,很多异步操作会通过Promise实现,如果Promise被reject之后没有对应的catch处理,就会触发unhandledRejection事件,这类异步异常同样需要处理。
处理示例如下:
// 监听未处理的Promise拒绝
process.on('unhandledRejection', (reason, promise) => {
console.error('捕获到未处理的Promise拒绝');
console.error('拒绝原因:', reason);
console.error('对应的Promise对象:', promise);
// 同样可以记录日志和清理资源
});
// 触发一个未处理的Promise拒绝
new Promise((resolve, reject) => {
reject(new Error('这是一个未处理的Promise拒绝'));
});
优雅处理的注意事项
- 不要在
uncaughtException回调中执行复杂的业务逻辑,避免因为异常导致应用状态异常,引发二次错误。 - 处理完异常后,建议通过
process.exit(1)主动退出进程,配合PM2等进程管理工具自动重启,比强行让进程继续运行更安全。 - 对于已知的异常场景,优先使用try/catch或者Promise的catch方法在对应逻辑中处理,未捕获异常处理应该作为兜底的容错方案,而不是常规处理手段。
- 记录异常信息时要包含完整的堆栈和上下文,方便后续排查问题。
完整的兜底处理示例
下面是一个同时处理同步和异步未捕获异常的完整示例,适合作为Node.js应用的入口兜底逻辑:
// 同步未捕获异常处理
process.on('uncaughtException', (err) => {
console.error(`[${new Date().toISOString()}] 未捕获同步异常: ${err.message}`);
console.error('堆栈信息:', err.stack);
// 清理资源
// 比如关闭数据库连接、停止监听端口等
// 主动退出进程
process.exit(1);
});
// 异步Promise未处理拒绝处理
process.on('unhandledRejection', (reason, promise) => {
console.error(`[${new Date().toISOString()}] 未捕获Promise拒绝: ${reason}`);
console.error('对应Promise:', promise);
process.exit(1);
});
// 模拟业务代码
const app = {
start() {
// 同步异常示例
setTimeout(() => {
throw new Error('业务同步异常');
}, 2000);
// 异步异常示例
setTimeout(() => {
new Promise((resolve) => {
throw new Error('业务异步异常');
});
}, 3000);
}
};
app.start();
通过上述方式,就可以在Node.js环境中对未捕获的异常进行兜底处理,最大程度降低异常对服务的影响,提升应用的稳定性。