Discord Bot倒计时命令多实例运行卡顿问题排查与优化
问题根源与优化方案
核心问题分析
- 全局变量污染:代码顶部的
eventMonth、eventDay、eventYear、time是全局变量,每次新的倒计时命令执行都会覆盖这些值,虽然当前逻辑暂未直接引发错误,但属于不良编码习惯,可能埋下隐患。 - 高频API请求:每个倒计时实例每秒调用一次
message.edit(),多实例运行时短时间内会发送大量Discord API请求,触发平台请求限制,导致Bot响应变慢甚至冻结。 - 冗余计算与错误逻辑:每次倒计时回调重复实例化moment对象,且存在
timeSpan <= -today的逻辑错误(today是moment对象,不能直接与数值比较)。
具体优化步骤
1. 移除全局变量,改用局部变量
将变量移至execute函数内部,确保每个倒计时实例的参数完全独立:
// 移除顶部全局变量 module.exports = { data: ..., async execute(interaction, client) { // 使用局部变量存储参数 const eventMonth = interaction.options.getInteger("event-month").toString().padStart(2, '0'); const eventDay = interaction.options.getInteger("event-day").toString().padStart(2, '0'); const eventYear = interaction.options.getInteger("event-year").toString(); const time = interaction.options.getString("event-time"); // ... 剩余代码 } };
2. 降低消息编辑频率
每秒更新消息属于过度冗余,用户无法感知如此精细的变化,改为每5秒更新一次,大幅减少API请求量:
// 设置5秒更新间隔 const updateInterval = 5000; interval = setInterval(countDownFn, updateInterval);
3. 优化时间计算逻辑
- 提前生成目标时间戳,避免每次回调重复实例化时间对象
- 修复时间差判断的逻辑错误
- 改用原生
DateAPI替代已停止维护的moment库,减少依赖并提升性能:
// 替换moment为原生Date处理时间转换 const convertTime12to24 = (time12h) => { const [time, modifier] = time12h.split(' '); let [hours, minutes] = time.split(':'); hours = parseInt(hours, 10); if (modifier === 'PM' && hours !== 12) hours += 12; if (modifier === 'AM' && hours === 12) hours = 0; return `${hours.toString().padStart(2, '0')}:${minutes}`; }; // 提前生成目标时间戳 const targetDate = new Date(`${eventYear}-${eventMonth}-${eventDay}T${convertTime12to24(time)}:00`); const eventTimestamp = targetDate.getTime(); // 优化后的倒计时回调 const countDownFn = () => { const now = Date.now(); const timeSpan = eventTimestamp - now; if (timeSpan <= 0) { clearInterval(interval); message.edit({ content: timeSpan >= -updateInterval ? "活动开始了!" : "活动已结束" }); return; } const days = Math.floor(timeSpan / (1000 * 60 * 60 * 24)); const hours = Math.floor((timeSpan % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const minutes = Math.floor((timeSpan % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((timeSpan % (1000 * 60)) / 1000); message.edit({ content: `Days: ${days} Hours: ${hours} - Minutes: ${minutes} - Seconds: ${seconds}` }); };
4. 批量管理定时器(可选)
维护全局定时器集合,方便Bot重启或关闭时统一清理,避免内存泄漏:
// 模块顶部添加定时器集合 const timers = new Set(); // 执行函数中添加定时器到集合 interval = setInterval(countDownFn, updateInterval); timers.add(interval); // 回调中清理时从集合移除 clearInterval(interval); timers.delete(interval); // Bot关闭时清理所有定时器 client.once('beforeExit', () => { timers.forEach(timer => clearInterval(timer)); });
优化后的完整代码
const { SlashCommandBuilder, PermissionFlagsBits } = require("discord.js"); const timers = new Set(); module.exports = { data: new SlashCommandBuilder() .setName("test-timer") .setDescription("Timer testing command") .setDefaultMemberPermissions(PermissionFlagsBits.ViewAuditLog) .addIntegerOption((option) => option .setName("event-month") .setDescription("month of the event") .setRequired(true) ) .addIntegerOption((option) => option .setName("event-day") .setDescription("day of the event") .setRequired(true) ) .addIntegerOption((option) => option .setName("event-year") .setDescription("year of the event") .setRequired(true) ) .addStringOption((option) => option .setName("event-time") .setDescription("time of the event (12h format, e.g. '3:30 PM')") .setRequired(true) ), async execute(interaction, client) { const message = await interaction.reply({ content: `Days: 0 Hours: 0 - Minutes: 0 - Seconds: 0`, fetchReply: true }); const eventMonth = interaction.options.getInteger("event-month").toString().padStart(2, '0'); const eventDay = interaction.options.getInteger("event-day").toString().padStart(2, '0'); const eventYear = interaction.options.getInteger("event-year").toString(); const time = interaction.options.getString("event-time"); const convertTime12to24 = (time12h) => { const [time, modifier] = time12h.split(' '); let [hours, minutes] = time.split(':'); hours = parseInt(hours, 10); if (modifier === 'PM' && hours !== 12) hours += 12; if (modifier === 'AM' && hours === 12) hours = 0; return `${hours.toString().padStart(2, '0')}:${minutes}`; }; const timeFormat = `${convertTime12to24(time)}:00`; const targetDate = new Date(`${eventYear}-${eventMonth}-${eventDay}T${timeFormat}`); const eventTimestamp = targetDate.getTime(); const updateInterval = 5000; let interval; const countDownFn = () => { const now = Date.now(); const timeSpan = eventTimestamp - now; if (timeSpan <= 0) { clearInterval(interval); timers.delete(interval); message.edit({ content: timeSpan >= -updateInterval ? "活动开始了!" : "活动已结束" }); return; } const days = Math.floor(timeSpan / (1000 * 60 * 60 * 24)); const hours = Math.floor((timeSpan % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const minutes = Math.floor((timeSpan % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((timeSpan % (1000 * 60)) / 1000); message.edit({ content: `Days: ${days} Hours: ${hours} - Minutes: ${minutes} - Seconds: ${seconds}` }); }; interval = setInterval(countDownFn, updateInterval); timers.add(interval); client.once('beforeExit', () => { timers.forEach(timer => clearInterval(timer)); }); } };
内容的提问来源于stack exchange,提问作者William
相关产品推荐
相关产品推荐

