unhandledRejection 用于捕获未处理的 Promise 拒绝,需记录完整错误日志、触发优雅关闭(如 server.close())、配合 uncaughtException 并禁用默认退出行为,避免资源泄漏。

在 Node.js 中,process.on('unhandledRejection') 是捕获未被 catch 的 Promise 拒绝的关键钩子。它本身不终止进程,但若放任不管,可能导致服务处于不可预期状态(如内存泄漏、连接堆积、响应挂起)。要“优雅闭合服务并日志记录”,核心是:**及时记录错误 + 主动触发服务关闭流程 + 避免强制退出(如 process.exit())导致资源未释放**。
明确 unhandledRejection 的定位与限制
该事件仅捕获未被 catch 或 .finally() 后续处理的 Promise rejection,不是兜底错误处理器。它不捕获同步异常、未捕获的异常(uncaughtException)或底层系统错误。因此需配合 uncaughtException 使用,且两者都应作为最后防线,而非替代良好的 Promise 错误处理习惯。
记录错误日志(含上下文与堆栈)
记录时不能只打 reason,要保留完整信息便于排查:
Node.js 可靠编程技能。避免事件循环阻塞、异步陷阱、ESM 兼容问题和内存泄漏。触发词:Node.js 开发、async/await、流处理、性能优化、安全编码。
- 使用结构化日志库(如
pino或winston),避免拼接字符串 - 记录
reason的message、stack、name;若reason是对象,用util.inspect(reason, { depth: 6 })安全序列化 - 添加时间戳、进程 PID、环境标识(如
process.env.NODE_ENV) - 示例(使用 pino):
logger.error({ err: reason, event: 'unhandledRejection' }, 'Unhandled promise rejection');
触发优雅关闭(Graceful Shutdown)
不能直接 process.exit(1)。应交由已实现的关闭逻辑执行:
立即学习“Java免费学习笔记(深入)”;
- 确保你的 HTTP server(如 Express、Fastify)、数据库连接池、消息队列客户端等都支持
close()/destroy()方法,并封装统一的shutdown()函数 - 在
unhandledRejection回调中,调用该函数,并传入错误原因用于日志上下文 - 设置超时兜底(如 30 秒),超时后强制退出,防止无限等待
- 示例伪代码:
process.on('unhandledRejection', async (reason, promise) => {<br> logger.error({ err: reason }, 'Unhandled rejection, starting graceful shutdown');<br> await shutdown({ reason }); // 包含 server.close(), db.end() 等<br>});
配合 uncaughtException 并禁用默认崩溃行为
为防遗漏,建议同时监听 uncaughtException,并在两者中均禁用默认退出(Node.js 默认会打印堆栈并退出):
- 在监听器开头加
process.exitCode = 1;,而不是立即process.exit() - 调用
process.exit()应仅放在shutdown()的最终回调或超时之后 - 注意:Node.js 15+ 默认将
unhandledRejection视为致命错误(会退出),所以必须显式监听以阻止默认行为

















