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

如何解决Discord SlashCommandBuilder无法异步加载MongoDB条目生成动态命令选项的问题?

解决Discord.js SlashCommandBuilder异步加载MongoDB数据作为选项的问题

你遇到的核心问题是命令加载流程是同步的,但数据库查询是异步的——当你的refreshCmds同步读取命令文件并把command.data转成JSON时,异步的数据库查询还没完成,所以选项根本没被添加到命令里。另外,原来的命令文件结构把data定义成了一个同步的Builder实例,没法容纳异步逻辑。

下面是完整的解决方案,我们会调整命令文件结构和命令加载流程来适配异步操作:

步骤1:修改命令文件结构,让data成为异步函数

把原来的data变量改成一个异步函数,这样我们可以在里面先完成数据库查询,再构建SlashCommandBuilder并添加选项。修改你的editfeature.js:

const { SlashCommandBuilder } = require("@discordjs/builders");
const { MessageEmbed, MessageActionRow, MessageButton } = require('discord.js');
const { ROLE } = require('../../../config/roles.js');
const config = require('../../../config/config.json')
const channel = require('../../../config/channel_ids.json')
const addfeatureSchema = require('../../database/models/addfeatureSchema.js');

// Set Role.<Role> if this command should be restricted to a specific role. Refer to /config/roles.js for the enums
const allowedRoles = [ROLE.Owner];

// 把data改成异步函数,先查询数据库再构建命令
async function data() {
  // 异步获取所有数据库条目
  const records = await addfeatureSchema.find();
  
  // 初始化SlashCommandBuilder
  const commandBuilder = new SlashCommandBuilder()
    .setName("editfeature")
    .setDescription('Edit a feature embed')
    .addStringOption(option => {
      option.setName('id')
        .setDescription('ID')
        .setRequired(true);
      
      // 循环为每个数据库条目添加选项
      for (const record of records) {
        option.addChoice(record.id, record.id);
      }
      
      return option;
    });
  
  return commandBuilder;
}

async function run(interaction) {
  const client = interaction.client;
  // 这里保留你的命令执行逻辑
}

module.exports = { allowedRoles, data, run };

步骤2:修改refreshCmds函数,支持异步加载命令

原来的refreshCmds是同步遍历文件,现在需要改成异步的,等待每个命令的data()函数执行完成,再获取构建好的Builder实例。修改你的index.js里的refreshCmds:

async function refreshCmds() {
  const commands = [];
  // 用异步的fs.promises.readdir替代同步方法,支持异步遍历
  const groups = await fs.promises.readdir(`${config.command_dir}`);
  
  for (const group of groups) {
    const commandFiles = await fs.promises.readdir(`${config.command_dir}${group}`);
    const jsFiles = commandFiles.filter(file => file.endsWith('.js'));
    
    for (const file of jsFiles) {
      const command = require(`${config.command_dir}${group}/${file}`);
      
      // 等待异步的data()函数执行完成,拿到完整的命令Builder
      const commandBuilder = await command.data();
      commands.push(commandBuilder.toJSON());
      client_commands.set(commandBuilder.name, command);
      
      console.log(`Loaded command file: ${group} -> ${file}`);
    }
  }
  
  try {
    console.log('Started refreshing application (/) commands.');
    await rest.put(Routes.applicationGuildCommands(client_id, guild_id), { body: commands });
    console.log('Successfully reloaded application (/) commands.');
  } catch (error) {
    console.error(error);
  }
}

为什么原来的代码不生效?

  1. 异步执行时机问题:你原来在addStringOption里调用getAThing().then(),这个回调是异步执行的——当return option的时候,循环还没开始,选项根本没被添加。等回调执行完,command.data已经被refreshCmds转成JSON并上传到Discord了,所以看不到选项。
  2. 同步加载流程:refreshCmds用同步的fs.readdirSync遍历文件,直接读取command.data(一个同步创建的Builder),没有等待异步操作完成的机会。

额外注意事项

  • 数据库查询错误处理:可以在data()函数里添加try/catch,避免数据库查询失败导致命令加载中断:
    async function data() {
      try {
        const records = await addfeatureSchema.find();
        // 构建命令...
      } catch (err) {
        console.error(`Failed to load records for editfeature command:`, err);
        // 返回一个降级版本的命令,或者抛出错误终止加载
        return new SlashCommandBuilder()
          .setName("editfeature")
          .setDescription('Edit a feature embed (Failed to load options)');
      }
    }
    
  • 命令更新时机:如果数据库里的条目发生变化,你需要重新调用refreshCmds()来更新命令选项——可以在条目修改的逻辑里触发这个函数,或者定时刷新。

内容的提问来源于stack exchange,提问作者LegalEagle

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 14:32:33