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

JavaScript倒计时器clearInterval失效问题排查求助

问题:WebSocket倒计时器无法在收到特定消息后停止并保持00:00显示

我编写了基于WebSocket的JavaScript倒计时器,需求是:当收到非start/stop事件或无event字段的消息时,计时器停止倒计时并固定显示00:00。但实际测试时,计时器正在倒计时状态下收到这类消息,会闪显00:00后立刻继续从之前的进度倒计时,clearInterval并未真正生效。

前端代码

/** WebSocket Setup */
//var ws = new WebSocket("CONFIG.WEBSOCKET_CONNECTION_PATH");

// jt localhost testing

var time = .25 * 60; // change first number for minutes to countdown
var constantTime = time;
var refreshIntervalId;
var start = false;
var countdown = document.getElementById("countdown");
let ws; // 全局声明ws,解决sendGeneralMessage的作用域问题

function connect() {

    ws = new WebSocket("ws:localhost:8000");

    ws.onopen = function (event) {
        sendGeneralMessage('history_timer_1', 'new_client');
    };

    ws.onmessage = function (event) {
        // parse into json, otherwise stop processing
        try {
            var payload = JSON.parse(event.data);
            console.log("RECEIVED: " + event.data);

            // continue processing if "event" field exists, stop processing otherwise
            if (payload.hasOwnProperty("event")) {
                console.log("has event field");
                // start clock is value is start, stop clock if value is stop, stop otherwise
                if (payload.event == "start" || payload.event == "stop") {
                    if (time != 0)
                        time = constantTime;

                    start = payload.event == "start";
                    console.log("start: " + start);

                    updateCountdown();
                }
                else {
                    console.log("value is not start or stop");
                    // 停止计时器并重置状态
                    clearInterval(refreshIntervalId);
                    start = false;
                    time = 0;
                    countdown.innerHTML = "00:00";
                }
            }
            else {
                console.log("has no event field");
                // 停止计时器并重置状态
                clearInterval(refreshIntervalId);
                start = false;
                time = 0;
                countdown.innerHTML = "00:00";

            }
        } catch(e) {
            console.log("RECEIVED: " + event.data);
            // 解析失败时同样停止计时器并重置状态
            clearInterval(refreshIntervalId);
            start = false;
            time = 0;
            countdown.innerHTML = "00:00";
        }

    };

    ws.onclose = function(event) {
        console.log("CONNECTION CLOSED");

        setTimeout(function() {
            connect();
        }, 1000);
    };

    ws.onerror = function(event) {
        console.log("ERROR");
    };
}

connect();

function sendGeneralMessage(clientID, event) {
    var message = {client_id:clientID, event: event};
    // console.log(JSON.stringify(message))
    ws.send(JSON.stringify(message));
}

function updateCountdown() {
    // 先清理旧定时器,避免多个定时器同时运行
    clearInterval(refreshIntervalId);
    
    if (!start)
        time = 0;

    refreshIntervalId = setInterval(function() {
        let minutes = Math.floor(time / 60);
        let seconds = time % 60;
        
        minutes = minutes < 10 ? '0' + minutes : minutes; //turns single digit minutes to double
        seconds = seconds < 10 ? '0' + seconds : seconds; //turns single digit seconds to double

        countdown.innerHTML = `${minutes}:${seconds}`;
        time--;
        if (time < 0) { //stop the setInterval to avoid negative time
            clearInterval(refreshIntervalId);
        }
    }, 1000);
}

测试用服务端代码

ws.on("message", (message) => {
        console.log("received %s", message);
        setTimeout(function () {
                var msg = JSON.stringify({event:"start"});
                ws.send(msg);
                console.log("sent %s", msg);
        }, 3000);
        setTimeout(function () {
            var msg = JSON.stringify("hello");
            ws.send(msg);
            console.log("sent %s", msg);
        }, 8000);
});

问题原因及修复说明

  1. 定时器重复创建:每次调用updateCountdown都会新建一个setInterval,但未清理之前的定时器,导致多个定时器同时修改DOM和time值。收到停止消息时仅清理了最后一个定时器ID,旧定时器仍在运行,所以会继续倒计时。
  2. 状态未同步重置:收到停止类消息时,只修改了DOM显示,但未重置start和time状态,旧定时器会继续基于原有time值更新。
  3. 变量作用域问题:原代码中ws是connect函数内的局部变量,全局的sendGeneralMessage无法访问,会导致发送消息失败,已改为全局声明ws。

关键修改点

  • 在updateCountdown开头添加clearInterval(refreshIntervalId),确保每次启动新定时器前清理旧的。
  • 所有需要停止计时器的分支(非start/stop事件、无event字段、JSON解析失败)中,除了清理定时器,还要设置start = false和time = 0,保证状态完全重置。
  • 将ws变量提升为全局变量,解决sendGeneralMessage的调用报错问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 22:05:52