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

Express全局使用questionList报错questionList is not iterable如何解决?

错误原因
  • 你声明的questionList是异步中间件函数,不是查询得到的题目数组,直接对函数进行遍历操作自然会触发not iterable报错
  • 中间件内next()调用顺序错误:你在执行数据库查询前就调用了next(),路由执行时数据库查询还未返回结果,就算你想拿函数返回值也拿不到
  • 逻辑缺陷:如果将题目列表设为全局变量,所有用户请求都会共用同一套题,且/attempts/submit接口如果重新查询随机题,和用户之前作答的题目无法匹配,得分计算会完全错误
修复方案

正确做法是将查询到的题目列表挂载到req对象上,同一次请求内的处理逻辑都可以直接取用,不同请求之间互不干扰。如果需要保证答题和提交用的是同一套题,提交时直接从之前存储的attempt记录中取对应题目判分即可,不需要重复查询随机题。

修复后代码如下:

const mongodb = require('mongodb');
const express = require('express');
const router = express.Router();
const app = express();
app.use(express.urlencoded());
app.use(express.json());

// 抽题中间件,将抽到的题目挂载到req上
var fetchQuestionList = async function (req, res, next) {
  try {
    req.questionList = await req.database
      .collection('questions')
      .aggregate([{ $sample: { size: 10 } }])
      .toArray();
    next();
  } catch (err) {
    next(err);
  }
};

// 只有抽题接口需要调用随机抽题中间件
router.post('/attempts', fetchQuestionList, async (req, res) => {
  let correctAnswers = {};
  for (const questions of req.questionList) {
    correctAnswers[questions._id] = questions.correctAnswer;
  }
  let attemptQuiz = {
    questions: req.questionList,
    correctAnswers,
    completed: false,
    startAt: new Date(),
  };

  let newAttemptQuiz = await req.database
    .collection('attempts')
    .insertOne(attemptQuiz);

  let renderAttemptsQuiz = await req.database
    .collection('attempts')
    .findOne({ _id: mongodb.ObjectId(`${newAttemptQuiz.insertedId}`) });

  const quesShow = renderAttemptsQuiz.questions;
  let objAll = [];
  for (const part of quesShow) {
    // 每次循环重新声明对象,避免引用复用问题
    let quest = {};
    quest._id = part._id;
    quest.text = part.text;
    quest.answers = part.answers;
    objAll.push(quest);
  }

  let responseAttempt = {
    _id: renderAttemptsQuiz._id,
    questions: objAll,
    completed: false,
    startAt: renderAttemptsQuiz.startAt,
  };
  res.status(200).json(responseAttempt);
});

// 提交接口不需要重新抽题,直接从存储的attempt记录取题判分
router.post('/attempts/submit', async (req, res) => {
  // 客户端提交时需要带上对应的attemptId
  const { attemptId, ...clientAnswers } = req.body;
  const attemptRecord = await req.database
    .collection('attempts')
    .findOne({ _id: mongodb.ObjectId(attemptId) });
  
  if (!attemptRecord) {
    return res.status(400).json({ error: '无效的答题记录' });
  }

  let count = 0;
  for (const ques of attemptRecord.questions) {
    if (attemptRecord.correctAnswers[ques._id] == clientAnswers[ques._id]) {
      count++;
    }
  }
  // 可补充更新答题记录为已完成的逻辑
  await req.database.collection('attempts').updateOne(
    { _id: mongodb.ObjectId(attemptId) },
    { $set: { completed: true, score: count, submitAt: new Date() } }
  );
  res.json({ score: count });
});

app.use(router);
module.exports = router;
注意点
  • 客户端调用/attempts/submit接口时,需要额外传回/attempts接口返回的_id作为attemptId参数,保证用同一套题判分
  • 修复了原代码中count用const声明无法累加、循环内对象引用复用的问题
  • 补充了错误捕获逻辑,避免未处理的Promise rejection报错

内容的提问来源于stack exchange,提问作者Nam Trường

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 15:27:02