You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.08 11:00:02