socket.io对接ioBroker时clearInterval无法终止定时器问题排查
解决ioBroker中setInterval定时器重复运行的问题
我在适配ioBroker的socket.io环境中,通过setInterval每秒展示最后接收时间戳的“多久之前”信息。首次运行正常,但当收到第二个更新后的时间戳时,旧定时器没有被clearInterval终止,导致新旧定时器同时运行。
需求:
- 当
onUpdate触发并收到新时间戳时,启动定时器每秒更新“多久之前”的显示 - 后续(比如2-3分钟后)收到新值时,必须终止当前定时器,再启动新定时器,展示新时间戳对应的信息(如“几秒前”“2分钟前”等)
问题根源
- timerId作用域错误:原来的
timerId是在getStates回调内部定义的局部变量,每次onUpdate都会创建新的timerId,导致无法访问到之前的定时器ID,无法清除旧定时器。 - 未主动清除旧定时器:创建新定时器前没有检查并清除已存在的定时器,导致多个定时器同时运行。
- 冗余的getStates调用:
stromvIDts是状态对象本身的ts字段,不需要再调用getStates获取,直接使用即可。
修复方案
- 将
timerId提升到全局作用域,确保每次onUpdate都能访问到同一个变量。 - 在创建新定时器前,先检查
timerId是否存在,若存在则调用clearInterval清除旧定时器。 - 移除冗余的
getStates([stromvIDts])调用,直接使用已有的时间戳。
修改后的完整代码
JavaScript代码
servConn.namespace = 'mobile.0'; servConn._useStorage = false; var stromv = 'hm-rpc.1.MEQ123456.1.POWER'; var subscriptions = [stromv]; var states = []; // 把timerId提升到全局作用域 var timerId = null; servConn.init({ name: 'mobile.0', connLink: 'http://IP:8082', socketSession: '' }, { onConnChange: function(isConnected) { console.log(isConnected ? 'connected' : 'disconnected'); }, onUpdate: function(id, state) { setTimeout(function() { states[id] = state; let stromvID = states[stromv].val; let stromvIDts = states[stromv].ts; // 更新当前数值显示 document.getElementById("stromvAktuell").innerHTML = stromvID + " W/h"; // 先清除旧定时器(如果存在) if (timerId) { clearInterval(timerId); } // 启动新定时器 timerId = setInterval(function() { let duration = moment(stromvIDts).fromNow(); console.log(duration); document.getElementById("stromvTs").innerHTML = duration; }, 1000); }, 0); }, onError: function(err) { window.alert(_('Cannot execute %s for %s, because of insufficient permissions', err.command, err.arg), _('Insufficient permissions'), 'alert', 600); } }, false, false); servConn._socket.emit('subscribe', subscriptions);
HTML代码(无需修改)
<!DOCTYPE html> <html lang="de"> <head> <link rel="icon" href="data:,"> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" /> <title>TEST</title> <script src="http://IP:8082/socket.io/socket.io.js"></script> <script src="../tmp/conn.js"></script> <script src="moment-with-locales.js"></script> <script> moment.locale('de') </script> </head> <body> <div> Value: <b id="stromvAktuell">unknown</b> <br> Last update: <b id="stromvTs">unknown</b> <br> </div> </body>
关键说明
- 全局timerId:确保每次
onUpdate都能引用到同一个定时器ID,清除旧定时器时不会找不到目标。 - 先清后建:每次收到新时间戳时,先终止旧定时器再创建新的,保证始终只有一个定时器在运行。
- 简化逻辑:移除不必要的
getStates调用,减少异步请求开销,提升代码效率。
内容的提问来源于stack exchange,提问作者Kai L.
相关产品推荐
相关产品推荐

