JavaScript async forEach异步循环未结束就执行后续代码问题
问题根因
执行顺序错乱的核心原因是Array.forEach本身不支持等待异步回调执行:传入forEach的async函数会被直接放到异步事件队列,forEach本身不会等回调执行完就直接往下走同步代码,所以会先执行console.log(2)和后续的删除判断,等所有同步代码执行完才会跑遍历里的异步逻辑、打印console.log(1)。这时候deletedMessagesIds还没完成数据填充,删除逻辑的判断结果自然不对。
修复方案
不要用forEach处理需要等待异步执行完成的遍历场景,替换为支持异步等待的遍历写法即可,两种常用实现可按需选择:
方案1:for...of 串行校验
和原本预期的执行顺序完全一致,会按数组顺序逐个校验消息状态,上一个校验完成才会处理下一个,逻辑直观易调试:
const { reactionRoleInformation, } = require("../database-functions/reactionroles/reactionRoleInformation"); const { deleteManyReactionRoles, } = require("../database-functions/reactionroles/deleteManyReactionRoles"); module.exports.cacheMessages = async (bot) => { const messageArray = await reactionRoleInformation(); const deletedMessagesIds = []; // 替换原forEach为for...of循环 for (const message of messageArray) { try { await bot.guilds.cache .get(message.guild) .channels.cache.get(message.channel) .messages.fetch(message.id); console.log(1); // 注:原代码此处重复调用了一次相同的messages.fetch,属于冗余逻辑已移除,有特殊业务需求可自行恢复 } catch (err) { deletedMessagesIds.push(message.uniqueId); } } console.log(2); if (deletedMessagesIds.length !== 0) { await deleteManyReactionRoles(deletedMessagesIds); } };
方案2:Promise.all 并行校验
如果消息量级不大、不需要严格按顺序校验,可以用并发校验的方式,执行效率比串行高很多:
const { reactionRoleInformation, } = require("../database-functions/reactionroles/reactionRoleInformation"); const { deleteManyReactionRoles, } = require("../database-functions/reactionroles/deleteManyReactionRoles"); module.exports.cacheMessages = async (bot) => { const messageArray = await reactionRoleInformation(); // 并发执行所有校验任务,统一等待所有任务执行完成 const checkResults = await Promise.all( messageArray.map(async (message) => { try { await bot.guilds.cache .get(message.guild) .channels.cache.get(message.channel) .messages.fetch(message.id); console.log(1); return null; } catch (err) { return message.uniqueId; } }) ); // 过滤掉校验通过返回的null,剩余就是需要删除的消息ID const deletedMessagesIds = checkResults.filter(id => id !== null); console.log(2); if (deletedMessagesIds.length !== 0) { await deleteManyReactionRoles(deletedMessagesIds); } };
内容的提问来源于stack exchange,提问作者KraftaN
相关产品推荐
相关产品推荐

