async函数错误不会自动进入Express/Koa全局错误中间件,需用try/catch捕获后调用next(err)或使用wrapAsync包装器统一处理,并配合四参数错误中间件,同时监听unhandledRejection兜底。

async 函数本身不会自动将错误抛给 Express 或 Koa 的全局错误处理中间件(如 app.use((err, req, res, next) => {...})),因为未被捕获的 Promise 拒绝(reject)会变成未处理的拒绝(unhandled rejection),而不会触发传统的同步错误流。要让 async 路由处理器的错误进入全局错误处理中间件,核心是**确保所有异步错误都以同步方式传递给 next()**。
用 try/catch 包裹 async 处理器
这是最直接、最可控的方式:在每个 async 路由回调中手动捕获错误,并调用 next(err)。
- Express/Koa 的错误中间件只响应显式调用
next(error)或同步抛出的错误 - Promise reject 若没被 catch,就脱离请求上下文,无法被中间件捕获
- 示例:
app.get('/user/:id', async (req, res, next) => {
try {
const user = await getUserById(req.params.id);
res.json(user);
} catch (err) {
next(err); // ✅ 错误进入全局错误中间件
}
});
封装通用的 async 包装器(推荐)
避免重复写 try/catch,可定义一个高阶函数自动处理。
- 它接收 async 回调,返回一个标准中间件函数
- 内部统一捕获异常并调用
next(err) - 支持路由、中间件、甚至嵌套中间件链
const wrapAsync = fn => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
// 使用
app.get('/post/:id', wrapAsync(async (req, res) => {
const post = await getPost(req.params.id);
res.json(post);
}));
配合全局错误中间件使用
包装器只是“送错”,还需定义能接收错误的中间件来统一响应。
- Express 中必须是四参数签名:
(err, req, res, next) - Koa 中通过
app.on('error', ...)或自定义错误处理中间件 - 注意:Express 错误中间件必须放在所有路由之后
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Something went wrong' });
});
警惕 Node.js 级别的 unhandledRejection
即使用了包装器,仍可能漏掉某些异步操作(如定时器、事件监听、未 await 的 Promise),导致进程级警告或崩溃。
- 建议添加兜底监听(仅用于日志和降级,不能替代
next(err)) - 开发环境开启
--trace-uncaught更容易定位问题
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
// 不要在这里 res.send() —— 响应对象已不存在
});

















