Discord机器人开发问题:如何终止运行中的函数并重新执行?
问题根源
你当前调用restart函数时仅启动了新的builderquestions流程,旧流程里创建的反应收集器(ReactionCollector)、等待消息的异步任务(awaitMessages)都没有被销毁,会继续运行并处理用户输入,导致旧流程和新流程同时生效。
解决思路
给每个频道存储对应流程的销毁方法和失效标记,每次重启前先销毁旧流程、标记旧流程失效,再启动新流程。
具体实现步骤
1. 新增全局存储
首先在全局作用域新增两个存储对象,按频道ID隔离不同的答题流程:
// 存储每个频道对应流程的清理函数 const channelTaskCleanup = new Map() // 存储每个频道的流程是否已失效 const channelTaskAborted = new Map()
2. 修改restart函数
重启前先执行旧流程的清理逻辑:
async function restart(channel) { // 清理旧流程 if (channelTaskCleanup.has(channel.id)) { channelTaskCleanup.get(channel.id)() channelTaskCleanup.delete(channel.id) } // 标记旧流程失效 channelTaskAborted.set(channel.id, true) // 启动新流程 builderquestions(channel) }
3. 改造builderquestions函数
在函数内增加清理逻辑,每一步执行前先校验当前流程是否有效:
async function builderquestions(channel) { const channelId = channel.id // 标记当前新流程为有效状态 channelTaskAborted.set(channelId, false) // 提前声明需要清理的对象 let intrebareaCollector, finalCollector, intrebarea, finalQuestion // 定义当前流程的清理函数 const cleanup = () => { channelTaskAborted.set(channelId, true) // 销毁所有收集器 if (intrebareaCollector) intrebareaCollector.stop() if (finalCollector) finalCollector.stop() // 可选:清理流程产生的旧消息 if (intrebarea) intrebarea.delete().catch(() => {}) if (finalQuestion) finalQuestion.delete().catch(() => {}) } // 把清理函数存入全局存储 channelTaskCleanup.set(channelId, cleanup) // ------------- 以下是你原有逻辑的修改版 ------------- intrebarea = await channel.send({ embed: { color, title: title[0], description: description[0], footer: { text: rescloseed } } }) await intrebarea.react('♻️') await intrebarea.react('❌') intrebareaCollector = intrebarea.createReactionCollector((reaction, user) => message.guild.members.cache.find((member) => member.id === user.id), { dispose: true }); intrebareaCollector.on("collect", async (reaction, user) => { // 流程已失效则不处理后续逻辑 if (channelTaskAborted.get(channelId)) return switch (reaction.emoji.name) { case "♻️": { reaction.users.remove(user.id); await restart(channel); break; } case "❌": { reaction.users.remove(user.id); // 关闭前先清理流程 cleanup() channelTaskCleanup.delete(channelId) channelTaskAborted.delete(channelId) closeticket(channel) break; } } }) const filter = m => m.author.id === message.author.id; // 所有awaitMessages的回调开头都先检查流程是否失效 channel.awaitMessages(filter, { max: 1, time, errors: ["time"] }) .then(async (collected) => { if (channelTaskAborted.get(channelId)) return const msg = collected.first(); map.set("answer2", msg.content) if (msg) msg.delete(); intrebarea.edit({ embed: { color, title: title[1], description: description[1], footer: { text: rescloseed } } }) return channel.awaitMessages(filter, { max: 1, time, errors: ["time"] }) }) .then(async (collected) => { if (!collected || channelTaskAborted.get(channelId)) return const msg = collected.first(); map.set("answer3", msg.content) if (msg) msg.delete(); intrebarea.edit({ embed: { color, title: title[2], description: description[2], footer: { text: rescloseed } } }) // 后续的所有答题步骤都可以改成这种平级的then,不用嵌套 // 更推荐的方案是用for循环遍历title数组实现答题逻辑,代码量可减少80%,不会出现回调地狱 }) // 最后的确认步骤收集器要存到finalCollector变量,方便清理 // 流程正常结束(用户点击✔️提交)后记得调用cleanup,删除全局存储里的对应条目,避免内存泄漏 }
额外优化建议
你当前8层嵌套的awaitMessages属于典型的回调地狱,完全可以通过for循环遍历title数组实现,不仅代码更简洁,后续维护和修改也更方便。
内容的提问来源于stack exchange,提问作者He1utu
相关产品推荐
相关产品推荐

