Node.js 18 默认继承 v15+ 策略,未捕获的 Promise 拒绝会直接终止进程。排查核心是监听 process 的 unhandledRejection 事件并定位异步错误源头,生产环境建议配合进程管理工具防止服务中断。
先说结论:Node.js 18 环境中未处理的 Promise 拒绝被视为致命错误,会导致进程退出,必须通过全局监听或代码层捕获处理。
- 先确认:检查 Node.js 版本是否大于等于 v15,确认默认退出行为。
- 先处理:添加 process.on('unhandledRejection') 全局监听器记录错误。
- 再验证:构造拒绝 Promise 测试进程是否按预期捕获或退出。
命令速用版
在入口文件顶部添加全局监听代码,可临时防止进程立即退出并记录错误堆栈:
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
// 生产环境建议记录日志后退出,避免状态不一致
process.exit(1);
});若使用 PM2 托管,配置 error_file 和 out_file 路径以便收集 stderr 输出。
为什么会这样
Node.js 从 v15 开始将未处理拒绝默认行为改为终止进程,Node.js 18 延续此策略。
早期版本仅输出警告,但静默失败会导致数据丢失或状态不一致。官方将其改为致命错误以强制开发者处理异步异常。浏览器端表现为控制台警告,而 Node.js 服务端直接 kill 进程,因此容器化部署时会出现重启循环。
分步处理
第一步:检查启动日志确认退出原因。查找 stderr 中是否有 UnhandledPromiseRejectionWarning 或直接退出记录。
第二步:全局拦截未知错误。在项目入口文件注册 unhandledRejection 和 uncaughtException 监听器,确保所有异步错误被记录。
第三步:定位具体代码。根据监听器输出的堆栈信息,找到未加 catch 的 Promise 链或未 await 的 async 函数调用。
第四步:修复业务代码。对 fire-and-forget 任务显式添加 .catch(),或在 async 函数外层包裹 try/catch。
怎么验证是否生效
创建测试文件 test.js,写入 Promise.reject(new Error('test')) 且不捕获。运行 node test.js,若进程退出码为 1 且控制台输出错误信息,说明默认行为生效。若已添加全局监听器,确认日志文件中有 reason 字段记录。
检查 PM2 日志命令:pm2 logs <app-name> `--lines` 100,查看是否有未处理拒绝记录。
常见坑
异步副作用陷阱:在 setTimeout 或事件回调中创建 Promise 却未处理,这类错误容易绕过顶层捕获。
忽略返回值:调用返回 Promise 的第三方 SDK 方法(如埋点、通知)时未 await 也未 catch,导致后台任务失败无声息。
混合使用回调与 Promise:部分旧代码混用 callback 和 Promise,错误传递链路断裂,导致拒绝无法被追踪。
常见问题
Node.js 18 能否配置为不退出进程?
可以,通过命令行参数 `--unhandled-rejections`=warn 改回警告模式,但生产环境不推荐。
全局监听器会影响性能吗?
几乎无影响,事件监听仅在发生未处理拒绝时触发,正常流程无额外开销。
PM2 自动重启能解决根本问题吗?
不能,自动重启仅掩盖症状,错误仍会发生且可能导致数据不一致,必须修复代码。
参考来源
- CSDN 博客:别再忽略 Promise 拒绝了!你的 Node.js 服务正在“静默自杀”
- 搜索结果:Promise 拒绝未捕获导致全局未处理错误
- 搜索结果:Promise 中的“未捕获异常” (Unhandled Rejection):如何全局监控?
- CSDN 博客:如何处理 Node.js 中的错误?特别是未处理的 Promise 拒绝