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
相关产品推荐
相关产品推荐

