async-await 仅是语法糖,不控制请求生命周期;真正决定流程的是中间件中是否正确调用 next() 或发送响应,且必须配合 try-catch + next(err) 实现错误传递,否则 promise rejection 将变为 unhandledrejection。

async-await 本身不直接控制请求生命周期,它只是让异步代码写起来像同步一样;真正控制请求生命周期的是中间件的调用时机、错误传播机制,以及你是否在合适的地方 调用 next() 或 结束响应(res.send/res.json/res.end)。
确保每个中间件都正确处理 await 并决定流程走向
在 Express/Koa 等框架中,中间件函数必须明确告诉框架“我处理完了”——要么调用 next() 继续下一个中间件,要么发送响应并终止流程。使用 async 函数时,await 不会自动触发 next(),你仍需手动控制。
- 忘记 await 可能导致逻辑提前执行(比如数据库查询还没返回就去校验空数据)
- await 之后没调用 next(),后续中间件不会执行,请求挂起(超时或客户端一直等待)
- await 之后调用了 next(),但又写了 res.send(),会抛出 “Cannot set headers after they are sent” 错误
统一错误捕获:用 try-catch 包裹 await,再交由全局错误中间件处理
Promise rejection 若未被捕获,会变成未处理的拒绝(Uncaught Promise Rejection),Node.js 会警告甚至退出进程。在中间件里,应把 await 放在 try-catch 中,并调用 next(err) 把错误交给错误处理中间件。
例如:
使用一条命令部署ProbeChain Rydberg测试网代理节点。自动注册为Agent(NodeType=1),免gas,支持macOS/Linux/Windows。触发词:/r
app.use(async (req, res, next) => {
try {
const user = await getUserById(req.params.id);
if (!user) throw new Error('User not found');
req.user = user;
next(); // ✅ 正常流程继续
} catch (err) {
next(err); // ✅ 错误交由下游错误中间件
}
});避免在中间件中隐式“吞掉”错误或响应
有些库或自定义逻辑可能内部 throw,也可能内部 res.send;若你 await 了一个封装了响应逻辑的函数,就不要再调用 next() 或 res.send —— 否则会冲突。
- 设计中间件时明确职责:是“纯逻辑中间件”(只加工 req/res,必调 next)还是“终结中间件”(负责发响应,不调 next)
- 对第三方 async 工具函数,先看文档是否已包含 send 行为;不确定时,可加一层包装判断 res.headersSent
- 终结中间件示例:
if (res.headersSent) return;前置防护可避免重复响应
配合 Koa 更自然:原生支持 async 中间件,且上下文更清晰
Express 的中间件签名是 (req, res, next),而 Koa 使用上下文 ctx 和洋葱模型,await next() 显式表示“等下游执行完再回来”,天然适合复杂流水线(如鉴权 → 日志 → 数据加载 → 缓存 → 渲染)。
Koa 示例:
app.use(async (ctx, next) => {
ctx.state.start = Date.now();
await next(); // 等所有下游中间件跑完
const ms = Date.now() - ctx.state.start;
ctx.set('X-Response-Time', `${ms}ms`);
});这种“进入-离开”对称性,让耗时统计、事务回滚、响应包装等跨阶段逻辑更容易实现。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










