You need to enable JavaScript to run this app. NodeJS MEN栈测验网站评分异常修复与异步逻辑优化咨询
优惠活动
大模型
产品
解决方案
定价
更多

NodeJS MEN栈测验网站评分异常修复与异步逻辑优化咨询

线上得分异常的核心根因

目前的异常是两个错误的异步时序写法叠加导致的,和代码本身的业务判断逻辑无关:

  • *forEach 嵌套 async 无等待能力:Array.prototype.forEach 是同步遍历方法,完全不会等待迭代回调里的Promise完成。ans.forEach 里的所有 await Question.findOne 操作只会被同步触发,主流程不会等待这些查询的结果返回就会进入下一步逻辑。
  • *setTimeout 固定延迟不具备可靠性:本地测试时MongoDB与服务同机部署,单题查询耗时仅为毫秒级,1秒的等待时长足够覆盖所有查询、正确累加得分。线上环境不管是云托管MongoDB的网络往返延迟,还是150个并发提交带来的数据库负载升高,单题查询耗时随时可能超过1秒。一旦延迟超过阈值,setTimeout 触发时 score 仍为初始值0,就会直接把0分写入用户记录,完全匹配“答案全对但线上得分0”的异常表现。
  • *存在变量名冲突隐患:迭代回调内定义的局部变量res,会覆盖路由作用域内的Express响应对象res,后续如果需要给前端返回JSON或错误提示,会直接出现调用失败。
重构方案

完全移除setTimeout逻辑,用async/await严格管控异步流程执行顺序,等所有题目校验、分数计算全部完成后,再执行数据库更新和页面返回。实现思路:

  • 将路由回调改为async函数,获得路由内部使用await的能力
  • 用Promise.all并发发起所有题目的校验查询,一次性等待所有查询结果返回,避免串行查询拖慢响应速度
  • 严格按顺序执行流程:答案预处理 → 并发查询所有题目完成校验 → 计算最终得分 → 更新用户测验记录 → 返回完成页

重构后的可直接运行的代码:

router.post("/submit", async (req, res) => {
  try {
    // 预处理用户提交的答案
    const submittedAnswers = Object.values(req.body);
    const processedUserAnswers = submittedAnswers.map(opt => {
      return opt.toLowerCase().split(" ").filter(Boolean);
    });

    // 并发发起所有题目的校验查询
    const checkPromises = processedUserAnswers.map(async (userAns, idx) => {
      const question = await Question.findOne({ index: idx });
      const correctAnswers = question.answer.toLowerCase().split(" ");
      // 匹配用户答案是否包含正确关键词
      return correctAnswers.some(correctWord => userAns.includes(correctWord));
    });

    // 等待所有题目校验完成
    const checkResults = await Promise.all(checkPromises);
    // 计算最终总分
    const totalScore = checkResults.filter(Boolean).length * 10;

    // 分数计算完成后再更新用户记录
    await User.findByIdAndUpdate(req.user.id, {
      score: totalScore,
      completed: true,
      submittedAt: Date.now(),
      answers: submittedAnswers
    });

    // 所有操作完成后再渲染页面
    res.render("finished");
  } catch (err) {
    console.error("提交接口错误:", err);
    res.status(500).render("error", { message: "提交失败,请重试" });
  }
});
方案可靠性说明
  • 无任何固定写死的等待时间,无论线上数据库延迟是100ms还是2s,代码都会等所有异步操作执行完成,彻底避免“分数未计算完成就先写入数据库”的时序问题
  • 并发查询的总耗时远低于单题串行查询,用户提交后的等待时长反而比原来的1秒固定等待更短
  • 增加了全局错误捕获逻辑,一旦数据库查询失败、用户ID不存在,会直接返回错误提示,不会出现静默写错分数、接口挂死的问题
  • 修复了原代码的变量名覆盖问题,全程不会干扰Express响应对象的正常使用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.13 04:54:04