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

Async/Await在Event Emitter中未按预期工作,请求MongoDB数据时不等待

网格寻宝游戏异步回调问题

我正在开发一款简单的网格寻宝游戏练手,前端采用HTML+JS实现,后端基于Node.js与Mongoose构建。游戏逻辑是玩家点击网格完成移动,当抵达宝藏tile时,后端需要从MongoDB获取winCode并返回给前端。

miApp.allGames是存储当前所有游戏实例的currentUserInfoMov对象数组,每个浏览器通过本地存储对应一个实例,这部分功能运行正常。

问题现象

movementsEmitter不会等待MongoDB的异步查询操作完成,控制台日志输出顺序如下:

  1. you win!
  2. Sent to front
  3. Emitter finished

相关代码

后端接口代码

routerApi.post("/clicked", async (req, res) => {
  try {
    miApp.movementsEmitter.emit("playerWantToMove", req.body, grid);
    const currentUserInfoMov = miApp.allGames.filter(x => x.Id == req.body.id)[0];
    res.send(currentUserInfoMov);
    console.log("Sent to Front");

  } catch (error) {
  console.log(error)  
  }
})

movementsEmitter监听代码

movementsEmitter.on("playerWantToMove", async (tileClickedAndId, grid) => {

...其他功能正常的代码...

 if (playerDestiny == treasureTile) {
  console.log("you win!");
  const bottle = await Bottles.findById(BOTTLEID);
  currentUserInfoMov.winCode = bottle.codeToWin;     
  TakeBottle();
  console.log("Emitter finished");
     
}
}

问题原因

emit触发的异步监听函数不会阻塞后续代码执行。接口中在调用emit后,立刻就从miApp.allGames中获取实例并返回响应,此时MongoDB的findById异步查询还未完成,winCode还没被赋值到currentUserInfoMov对象中。

解决方案

方案1:用Promise包装事件触发,等待异步处理完成

修改后端接口代码,将emit操作包装为Promise,确保等待事件监听中的异步逻辑执行完毕后再返回响应:

routerApi.post("/clicked", async (req, res) => {
  try {
    // 包装事件触发为Promise,等待异步逻辑完成
    await new Promise((resolve) => {
      miApp.movementsEmitter.emit("playerWantToMove", req.body, grid, resolve);
    });
    // 用find替代filter提升效率
    const currentUserInfoMov = miApp.allGames.find(x => x.Id == req.body.id);
    res.send(currentUserInfoMov);
    console.log("Sent to Front");
  } catch (error) {
    console.error(error);
    res.status(500).send("服务器错误");
  }
})

同时修改movementsEmitter的监听代码,接收resolve参数并在异步逻辑完成后调用:

movementsEmitter.on("playerWantToMove", async (tileClickedAndId, grid, resolve) => {

...其他功能正常的代码...

 if (playerDestiny == treasureTile) {
  console.log("you win!");
  const bottle = await Bottles.findById(BOTTLEID);
  currentUserInfoMov.winCode = bottle.codeToWin;     
  TakeBottle();
  console.log("Emitter finished");
 }
 // 通知Promise异步处理完成
 resolve();
})

方案2:将响应逻辑移至事件监听中(耦合性较高)

如果不想修改事件传递方式,可以把返回响应的逻辑放到监听函数里,注意要传递req和res对象:

routerApi.post("/clicked", async (req, res) => {
  try {
    miApp.movementsEmitter.emit("playerWantToMove", req.body, grid, req, res);
  } catch (error) {
    console.error(error);
    res.status(500).send("服务器错误");
  }
})

监听代码修改:

movementsEmitter.on("playerWantToMove", async (tileClickedAndId, grid, req, res) => {

...其他功能正常的代码...

 let currentUserInfoMov = miApp.allGames.find(x => x.Id == tileClickedAndId.id);
 if (playerDestiny == treasureTile) {
  console.log("you win!");
  const bottle = await Bottles.findById(BOTTLEID);
  currentUserInfoMov.winCode = bottle.codeToWin;     
  TakeBottle();
  console.log("Emitter finished");
 }
 res.send(currentUserInfoMov);
 console.log("Sent to Front");
})

内容的提问来源于stack exchange,提问作者dandomin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 02:55:23