Discord.js命令部署脚本执行完成后无法终止的问题
问题描述
我基于Discord.js官方指南修改了命令部署脚本,脚本已成功刷新应用(/)命令并输出成功日志,但进程始终无法终止,不清楚原因。
脚本代码如下:
const commands = []; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const foldersPath = path.join(__dirname, 'commands'); const commandFolders = fs.readdirSync(foldersPath); for (const folder of commandFolders) { // Grab all the command files from the commands directory you created earlier const commandsPath = path.join(foldersPath, folder); const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js')); // Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment for (const file of commandFiles) { const filePath = path.join(commandsPath, file); const command = await import(filePath); if ('commandObject' in command && 'execute' in command) { commands.push(command.commandObject.commandInfo); } else { console.warn(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`); } } } // Construct and prepare an instance of the REST module const rest = new REST().setToken(botToken); // and deploy your commands! (async() => { try { console.log(`Started refreshing ${commands.length} application (/) commands.`); // The put method is used to fully refresh all commands in the guild with the current set const data = await rest.put( Routes.applicationGuildCommands(clientId, guildId), { body: commands } ); console.log(`Successfully reloaded ${data.length} application (/) commands.`); } catch (error) { console.error(error); } })();
问题原因与解决办法
进程无法终止的核心原因是事件循环中存在未清理的异步资源,以下是具体排查和解决方向:
1. 清理命令文件中的异步残留资源
你通过await import(filePath)加载的命令文件里,可能存在未关闭的定时器(如setInterval)、数据库连接、WebSocket连接等。这些资源会占用事件循环,阻止进程自动退出。
- 逐一检查所有命令文件,确保这类资源在模块加载后没有长期运行,必要时添加关闭逻辑。
2. 手动销毁REST模块的连接池
Discord.js的REST模块默认使用持久连接池,请求完成后连接池可能仍保持活跃,导致进程无法退出。
- 在部署成功的日志输出后,添加
rest.destroy()销毁连接池:console.log(`Successfully reloaded ${data.length} application (/) commands.`); rest.destroy(); // 添加这行
3. 主动终止进程
如果上述方法无效,可以在所有操作完成后主动调用process.exit(0)强制终止进程。同时建议把顶级的异步逻辑都包裹到一个自执行异步函数中,避免顶级await的潜在问题:
修改后的完整代码示例:
(async () => { const commands = []; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const foldersPath = path.join(__dirname, 'commands'); const commandFolders = fs.readdirSync(foldersPath); for (const folder of commandFolders) { const commandsPath = path.join(foldersPath, folder); const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js')); for (const file of commandFiles) { const filePath = path.join(commandsPath, file); const command = await import(filePath); if ('commandObject' in command && 'execute' in command) { commands.push(command.commandObject.commandInfo); } else { console.warn(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`); } } } const rest = new REST().setToken(botToken); try { console.log(`Started refreshing ${commands.length} application (/) commands.`); const data = await rest.put( Routes.applicationGuildCommands(clientId, guildId), { body: commands } ); console.log(`Successfully reloaded ${data.length} application (/) commands.`); rest.destroy(); } catch (error) { console.error(error); rest.destroy(); } process.exit(0); // 主动终止进程 })();
4. 排查模块缓存的影响
动态import()会缓存加载的模块,如果模块中存在长期运行的代码,可能导致进程残留。可以尝试在启动脚本时添加--no-cache参数,或者在加载后清理模块缓存(仅在必要时使用)。
内容的提问来源于stack exchange,提问作者Caleb Renfroe
相关产品推荐
相关产品推荐

