使用forEach与async/await填充数组失败,外部数组为空求助
问题分析与解决方案:异步forEach导致数组未填充
嗨,我一眼就看出问题所在啦——你遇到的是forEach不支持异步等待的典型坑!
为什么会出现这种情况?
你在forEach的回调函数里用了async/await,但forEach的设计根本不会等待异步任务完成。它会快速遍历数组,把每个异步回调都丢进事件队列里,然后立刻继续执行后面的代码(也就是你打印"2"的那一行)。这时候那些Category.findOne的异步请求还在后台跑,categories.push还没执行,所以数组自然是空的。而当异步请求完成后,才会执行push,这时候你打印"1"就能看到填充后的数组,但这已经是在"2"之后的事了。
解决方案1:用for...of循环(串行执行)
for...of是支持async/await的循环结构,它会等待每次循环里的异步操作完成后,再进入下一次循环。这样当你走到console.log("2")时,所有的分类查询都已经完成,categories数组也被正确填充了。
修改后的代码:
let categories: Category[] = []; try { // 替换forEach为for...of循环 for (const categoryId of categoryIds) { const category = await Category.findOne({ where: { id: categoryId } }); if (category) { categories.push(category); } console.log("1", categories); // 每次循环都会正确填充 } console.log("2", categories); // 现在这里会输出填充后的数组 if (categories.length === 0) { return null; } console.log("3", categories); // 现在能正常走到这里了 const newQuestion = new Question(); newQuestion.title = title; newQuestion.text = text; newQuestion.categories = categories; const question = await newQuestion.save(); return question; } catch (error) { console.log(error); return null; }
解决方案2:用Promise.all(并行执行,效率更高)
如果你的分类查询不需要按顺序执行,用Promise.all可以让所有查询并行发起,比串行的for...of效率更高。我们先把所有查询转换成Promise数组,等待全部完成后再过滤掉null的结果:
修改后的代码:
try { // 并行发起所有分类查询,然后过滤掉不存在的分类 const categories = await Promise.all( categoryIds.map(async (categoryId) => { return Category.findOne({ where: { id: categoryId } }); }) ).then(results => results.filter(category => category !== null)); console.log("2", categories); // 这里已经是完整的分类数组 if (categories.length === 0) { return null; } console.log("3", categories); const newQuestion = new Question(); newQuestion.title = title; newQuestion.text = text; newQuestion.categories = categories; const question = await newQuestion.save(); return question; } catch (error) { console.log(error); return null; }
两种方案对比
for...of:串行执行异步操作,适合需要按顺序处理每个请求的场景(比如前一个请求的结果会影响下一个)。Promise.all:并行执行所有异步操作,效率更高,适合像这种独立的查询场景。
选哪种就看你的具体需求啦,两种都能解决你现在的问题~
内容的提问来源于stack exchange,提问作者Hendry Lim
相关产品推荐
相关产品推荐

