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); });
问题原因及修复说明
- 定时器重复创建:每次调用
updateCountdown都会新建一个setInterval,但未清理之前的定时器,导致多个定时器同时修改DOM和time值。收到停止消息时仅清理了最后一个定时器ID,旧定时器仍在运行,所以会继续倒计时。 - 状态未同步重置:收到停止类消息时,只修改了DOM显示,但未重置
start和time状态,旧定时器会继续基于原有time值更新。 - 变量作用域问题:原代码中
ws是connect函数内的局部变量,全局的sendGeneralMessage无法访问,会导致发送消息失败,已改为全局声明ws。
关键修改点
- 在
updateCountdown开头添加clearInterval(refreshIntervalId),确保每次启动新定时器前清理旧的。 - 所有需要停止计时器的分支(非start/stop事件、无event字段、JSON解析失败)中,除了清理定时器,还要设置
start = false和time = 0,保证状态完全重置。 - 将
ws变量提升为全局变量,解决sendGeneralMessage的调用报错问题。
内容的提问来源于stack exchange,提问作者spjennie
相关产品推荐
相关产品推荐

