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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 18:05:18