You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express.js中app.post内使用await并外部获取请求体时多次请求出现冻结的问题

问题分析与解决方案

你的代码出现冻结的核心问题在于每次调用receivePost都会给同一个url注册一个新的POST路由处理器,而不是替换已有的。当发起两次及以上请求时,同一个URL会触发多个注册的处理器,导致Promise逻辑混乱、请求处理流程冲突,最终引发冻结。

另外还有两个易忽略的问题:

  • 不需要手动调用req.end(),Express在调用res.send()后会自动完成请求的收尾工作,手动调用反而可能干扰正常流程。
  • 你的Promise只会resolve第一次请求的req.body,后续请求触发的resolve不会有任何效果——因为Promise的状态一旦改变就无法再修改。

修正方案:避免重复注册路由,改用事件驱动处理多次请求

我们可以通过Node.js的EventEmitter来实现多次请求的数据传递,确保每个URL只注册一次路由处理器,后续请求通过事件通知的方式传递数据。

修正后的代码示例

const { EventEmitter } = require('events');

class YourClass {
  constructor() {
    this.app = /* 你的Express app实例 */;
    this.routeEvents = new EventEmitter();
    // 维护已注册路由的集合,避免重复添加处理器
    this.registeredRoutes = new Set();
  }

  receivePost(url, response) {
    // 检查路由是否已注册,未注册才添加处理器
    if (!this.registeredRoutes.has(url)) {
      this.app.post(url, async (req, res) => {
        // 先返回响应给客户端
        res.send(response);
        // 通过事件发送请求体数据给外部
        this.routeEvents.emit(`post:${url}`, req.body);
      });
      this.registeredRoutes.add(url);
    }

    // 返回Promise用于获取单次请求数据;若需多次获取,可直接监听对应事件
    return new Promise((resolve) => {
      const handler = (body) => {
        resolve(body);
        // 移除监听器,避免内存泄漏
        this.routeEvents.off(`post:${url}`, handler);
      };
      this.routeEvents.once(`post:${url}`, handler);
    });
  }
}

方案说明

  1. 避免重复注册路由:通过registeredRoutes集合记录已注册的URL,确保每个URL只绑定一次POST处理逻辑。
  2. 事件驱动传递数据:使用EventEmitter,每次收到请求时触发对应URL的事件,外部通过监听事件(或返回的Promise)获取请求数据。
  3. 规范请求收尾:移除了不必要的req.end(),依赖Express自身的请求处理流程完成收尾。

为什么重启服务器没用?

因为每次调用receivePost都会在当前的app实例上添加新的路由处理器,即使重启服务器,如果你的代码逻辑还是会重复调用receivePost注册路由,问题依然会出现。只有从根源上避免重复注册路由,才能彻底解决这个问题。

内容的提问来源于stack exchange,提问作者Алексей Алексеев

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 16:28:15