全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-15 7 分钟 ✍️ juanwangdev

Node.js 全局未捕获异常处理

全局异常处理是错误未被捕获时的最后防线。

uncaughtException

捕获未处理同步异常

JavaScript
// 未被 try/catch 捕获的异常
process.on('uncaughtException', (err, origin) => {
  console.error('未捕获异常:', err.message);
  console.error('来源:', origin);
  console.error('堆栈:', err.stack);

  // 记录日志
  logger.error('Uncaught Exception', err);

  // 必须退出进程
  process.exit(1);
});

触发场景

JavaScript
// 同步代码抛出异常未被捕获
function risky() {
  throw new Error('崩溃');
}

risky();  // 无 try/catch,触发 uncaughtException

处理策略

JavaScript
process.on('uncaughtException', (err) => {
  // 区分可恢复和不可恢复
  if (err.isOperational) {
    logger.warn('可恢复错误:', err.message);
    // 可选择不退出,继续运行
  } else {
    logger.error('不可恢复错误:', err.stack);
    // 不可恢复错误必须退出
    process.exit(1);
  }
});

unhandledRejection

捕获未处理 Promise 拒绝

JavaScript
// 未被 .catch() 或 try/catch 捕获的 Promise 错误
process.on('unhandledRejection', (reason, promise) => {
  console.error('未处理 Promise 拒绝:');
  console.error('原因:', reason);
  console.error('Promise:', promise);

  // 记录日志
  logger.error('Unhandled Rejection', reason);
});

触发场景

JavaScript
// Promise 没有 catch
Promise.reject('错误');
// 触发 unhandledRejection

// async 函数没有 try/catch
async function risky() {
  throw new Error('崩溃');
}
risky();  // 无 try/catch,触发 unhandledRejection

处理策略

JavaScript
process.on('unhandledRejection', (reason) => {
  if (reason instanceof Error) {
    logger.error('未处理错误:', reason.stack);
  } else {
    logger.error('未处理拒绝:', reason);
  }

  // Node.js 15+ 默认退出
  // 可选择记录后继续或退出
});

rejectionHandled

捕获延迟处理的拒绝

JavaScript
// Promise 拒绝后延迟添加 catch
process.on('rejectionHandled', (promise) => {
  console.log('Promise 拒绝后被处理');
});

const p = Promise.reject('延迟处理');
// 此时触发 unhandledRejection

setTimeout(() => {
  p.catch(err => console.log(err));
  // 此时触发 rejectionHandled
}, 1000);

完整全局处理方案

标准配置

JavaScript
// 全局异常处理
process.on('uncaughtException', (err) => {
  logger.error('Uncaught Exception', {
    message: err.message,
    stack: err.stack,
    code: err.code
  });

  // 优雅关闭
  gracefulShutdown(() => {
    process.exit(1);
  });
});

process.on('unhandledRejection', (reason, promise) => {
  logger.error('Unhandled Rejection', {
    reason: reason instanceof Error ? reason.stack : reason,
    promise: promise
  });
});

// Node.js 15+ 可设置严格模式
// process.on('unhandledRejection', (reason) => {
//   throw reason;  // 转为 uncaughtException
// });

优雅关闭

JavaScript
let isShuttingDown = false;

function gracefulShutdown(callback) {
  if (isShuttingDown) return;
  isShuttingDown = true;

  console.log('开始优雅关闭...');

  // 停止接收新请求
  server.close();

  // 等待现有请求完成
  setTimeout(() => {
    console.log('关闭完成');
    callback();
  }, 5000);
}

process.on('SIGTERM', () => gracefulShutdown(() => process.exit(0)));
process.on('SIGINT', () => gracefulShutdown(() => process.exit(0)));

使用 domain(已废弃)

JavaScript
// domain 模块已废弃,不建议使用
const domain = require('domain');

const d = domain.create();
d.on('error', (err) => {
  console.error('Domain 捕获:', err);
});

d.run(() => {
  riskyOperation();
});

PM2 与全局异常

JavaScript
// PM2 配合全局异常处理
process.on('uncaughtException', (err) => {
  logger.error(err);
  process.exit(1);  // PM2 自动重启
});

// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'app',
    script: 'app.js',
    autorestart: true,
    max_restarts: 10,  // 最大重启次数
    restart_delay: 3000  // 重启延迟
  }]
};

最佳实践

不依赖全局处理

JavaScript
// 错误:依赖全局处理
async function risky() {
  throw new Error('错误');
}
risky();  // 等待全局捕获

// 正确:主动处理
async function risky() {
  try {
    throw new Error('错误');
  } catch (err) {
    logger.error(err);
  }
}

// 或调用处处理
risky().catch(err => logger.error(err));

区分错误类型

JavaScript
class OperationalError extends Error {
  constructor(message) {
    super(message);
    this.isOperational = true;
  }
}

process.on('uncaughtException', (err) => {
  if (err.isOperational) {
    logger.warn('可恢复错误:', err.message);
  } else {
    logger.error('程序错误:', err.stack);
    process.exit(1);
  }
});

注意事项

  • uncaughtException 后进程状态不稳定,应退出
  • Node.js 15+ unhandledRejection 默认退出进程
  • 全局处理是最后防线,不是日常手段
  • 必须记录错误日志便于排查
  • 使用 PM2 自动重启恢复

要点总结

  • uncaughtException 捕获未处理同步异常
  • unhandledRejection 捕获未处理 Promise 拒绝
  • 捕获后记录日志并优雅退出
  • 使用 isOperational 区分可恢复错误
  • PM2 配合自动重启保证稳定

📝 发现内容有误?点击此处直接编辑

← 上一篇 Node.js try/catch/finally
下一篇 → Node.js 异步错误处理(回调、Promise、async/await)
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库