异步函数未等待代码执行完成,广播空数组问题求助
问题:异步任务未完成就执行广播,导致首次广播空数组
我正在编写一个从API拉取消息并筛选含图片消息的脚本,该API有多个端点对应不同消息批次,需要收集所有符合条件的消息(示例中有3个链接,会打印3次"test")。
当前问题是messageGetter()函数尚未完全执行完毕,broadcast()就被调用了,导致首次执行脚本时广播空数组,第二次执行才能获取到所需内容。
现有代码
module.exports = { async execute(app) { await fetchImages(app); await interaction.reply("Getting Images"); }, }; async function fetchImages(app) { app.link.forEach(async (link) => { if (link.type === 0) { await messageGetter(link); } }); console.log("here"); broadcast({ type: "imageLinks", data: imageLinks }); imageLinks.splice(0, imageLinks.length); } async function messageGetter(link, limit = 500) { const sum_messages = []; let last_id; while (true) { const options = { limit: 100 }; if (last_id) { options.before = last_id; } const messages = await link.messages.fetch(options); sum_messages.push(...messages.values()); last_id = messages.last().id; if (messages.size != 100 || sum_messages >= limit) { break; } } sum_messages.forEach((message) => { if (message.attachments.size > 0) { message.attachments.forEach((attachment) => { if (attachment.url.match(/\.(jpeg|jpg|gif|png)$/i)) { imageLinks.push(attachment.url); } }); } }); console.log("test"); }
日志输出
here test test test
修复方案
问题根源
forEach循环不支持异步等待,循环内的async回调会被立即触发,但fetchImages函数不会等待所有回调中的await messageGetter(link)执行完成,就直接向下执行broadcast,导致此时imageLinks还未收集到数据。
修复步骤
将forEach替换为Promise.all,让fetchImages等待所有messageGetter异步任务完成后再执行广播:
修改后的fetchImages函数
async function fetchImages(app) { // 过滤符合条件的链接,生成异步任务数组 const tasks = app.link .filter(link => link.type === 0) .map(link => messageGetter(link)); // 等待所有异步任务全部完成 await Promise.all(tasks); console.log("here"); broadcast({ type: "imageLinks", data: imageLinks }); imageLinks.splice(0, imageLinks.length); }
额外优化(可选)
原代码中imageLinks是全局变量,容易引发多次执行时的状态污染,建议改为局部变量,并通过参数传递给messageGetter,同时修正sum_messages >= limit的错误判断(数组需用length属性比较):
优化后完整代码
module.exports = { async execute(app) { await fetchImages(app); await interaction.reply("Getting Images"); }, }; async function fetchImages(app) { // 局部变量存储图片链接,避免全局状态污染 const imageLinks = []; const tasks = app.link .filter(link => link.type === 0) .map(link => messageGetter(link, imageLinks)); await Promise.all(tasks); console.log("here"); broadcast({ type: "imageLinks", data: imageLinks }); } async function messageGetter(link, imageLinks, limit = 500) { const sum_messages = []; let last_id; while (true) { const options = { limit: 100 }; if (last_id) { options.before = last_id; } const messages = await link.messages.fetch(options); sum_messages.push(...messages.values()); last_id = messages.last().id; // 修正数组长度判断逻辑 if (messages.size != 100 || sum_messages.length >= limit) { break; } } sum_messages.forEach((message) => { if (message.attachments.size > 0) { message.attachments.forEach((attachment) => { if (attachment.url.match(/\.(jpeg|jpg|gif|png)$/i)) { imageLinks.push(attachment.url); } }); } }); console.log("test"); }
内容的提问来源于stack exchange,提问作者Pazulay
相关产品推荐
相关产品推荐

