← 中文资源

简体中文版本

Node.js — 未处理的 Promise 拒绝

找出没有处理器的拒绝 Promise,修复浮空的异步调用和异步事件处理器,并添加进程级安全网而不吞掉错误。

阅读英文原文

被拒绝的 Promise 需要处理器

Promise 拒绝且无人处理时,Node 报告未处理拒绝。从 Node 15 起,默认行为是终止进程。堆栈常指向 Promise 链,而不是忘记 await 的调用点。使用 --trace-warnings --unhandled-rejections=strict 获取更有用的追踪。

// Floating: the rejection escapes
loadUser(id).then((user) => res.json(user));

// Handled: await inside try/catch
try {
  const user = await loadUser(id);
  res.json(user);
} catch (error) {
  next(error);
}

异步事件处理器

事件发射器不会 await 异步监听器。异步 on('message') 处理器返回的拒绝 Promise 会变成未处理拒绝。应把主体包在 try/catch 中,或使用附加 catch 处理器并上报失败的辅助函数。setTimeout(async () => ...) 同理。

Fire-and-forget 也需要 catch

有些工作是有意分离的,例如指标刷新。要附加 .catch() 并决定失败时的行为:记录、重试或标记降级。不要用 void 让编译器闭嘴,却把拒绝留成未处理。

void flushMetrics().catch((error) => logger.error({ error }, 'metrics flush failed'));

决定进程策略

进程级 unhandledRejection 处理器可以记录并退出,这比带着未知状态继续运行更安全。如果服务可以恢复,要记录哪些错误可恢复以及原因。通过强制制造拒绝并断言退出码或恢复行为来测试处理器。

修复与验证

在 try/catch 中 await 异步调用、为有意分离的工作附加 .catch()、包装异步事件处理器以处理返回的 Promise,从而修复源头。用强制依赖拒绝的测试验证,使用 --unhandled-rejections=strict 运行服务,确认错误带着操作上下文被记录,且进程遵循文档化的退出或恢复策略。为浮空 Promise 添加 lint 或评审检查。