ExtJS Ext.util.TaskRunner仅在页面获焦时运行的问题排查
解决Chrome非激活标签页中ExtJS TaskRunner任务暂停问题
Chrome会对非激活标签页的setInterval/setTimeout进行节流限制,这导致ExtJS TaskRunner的定时任务在后台标签页中变慢甚至暂停,进而影响自动登出的倒计时准确性。以下是两种可行的解决方法:
方法一:改用基于时间戳的倒计时逻辑
放弃依赖TaskRunner的任务运行次数,转而通过记录用户最后活动时间与当前时间的差值计算剩余会话时长,即使定时器被节流,回到页面时也能立刻算出准确的剩余时间。
修改代码示例:
// 初始化时记录用户最后活动时间 this.lastActivityTime = Date.now(); this.timeoutInSec = 20 * 60; this.runner = new Ext.util.TaskRunner(); this.autoLogoutTask = this.runner.newTask({ run: this.checkTimeout, interval: this.checkInterval, // 保留原间隔,但不再依赖运行次数 scope: this }); this.firstWarningDisplay = false; this.secondWarningDisplay = false; this.autoLogoutTask.start(); // 更新checkTimeout方法 checkTimeout() { const elapsedSec = Math.floor((Date.now() - this.lastActivityTime) / 1000); const remainingSec = this.timeoutInSec - elapsedSec; if (remainingSec <= 0) { // 执行自动登出逻辑 this.doAutoLogout(); this.autoLogoutTask.stop(); return; } // 每分钟输出一次倒计时(仅当剩余时间为整分钟时) if (remainingSec % 60 === 0) { console.log(`Session will expire in ${remainingSec / 60} min`); } // 处理警告逻辑 if (remainingSec === 5 * 60 && !this.firstWarningDisplay) { this.showFirstWarning(); this.firstWarningDisplay = true; } if (remainingSec === 60 && !this.secondWarningDisplay) { this.showSecondWarning(); this.secondWarningDisplay = true; } } // 监听用户活动,更新最后活动时间 Ext.getDoc().on('mousemove keydown', () => { this.lastActivityTime = Date.now(); // 重置警告状态 this.firstWarningDisplay = false; this.secondWarningDisplay = false; });
这种方法无需额外资源,修改成本低,核心是用实际时间差替代定时器运行次数,彻底摆脱对定时器准确性的依赖。
方法二:使用Web Worker运行定时器
Web Worker在独立线程中运行,不受Chrome标签页激活状态的节流限制,能保证定时器稳定执行。
步骤1:创建Worker文件(auto-logout-worker.js)
let intervalId; self.addEventListener('message', (e) => { const { type, interval } = e.data; if (type === 'start') { intervalId = setInterval(() => { self.postMessage('tick'); }, interval); } else if (type === 'stop') { clearInterval(intervalId); } });
步骤2:修改主页面代码
this.timeoutInSec = 20 * 60; this.firstWarningDisplay = false; this.secondWarningDisplay = false; this.lastActivityTime = Date.now(); // 创建Worker this.logoutWorker = new Worker('auto-logout-worker.js'); this.logoutWorker.postMessage({ type: 'start', interval: this.checkInterval }); // 监听Worker的tick事件 this.logoutWorker.addEventListener('message', () => { this.checkTimeout(); }); // 实现checkTimeout方法(同方法一的逻辑) checkTimeout() { const elapsedSec = Math.floor((Date.now() - this.lastActivityTime) / 1000); const remainingSec = this.timeoutInSec - elapsedSec; if (remainingSec <= 0) { this.doAutoLogout(); this.logoutWorker.postMessage({ type: 'stop' }); return; } if (remainingSec % 60 === 0) { console.log(`Session will expire in ${remainingSec / 60} min`); } // 警告逻辑... } // 监听用户活动 Ext.getDoc().on('mousemove keydown', () => { this.lastActivityTime = Date.now(); this.firstWarningDisplay = false; this.secondWarningDisplay = false; }); // 页面销毁时关闭Worker this.on('destroy', () => { this.logoutWorker.postMessage({ type: 'stop' }); this.logoutWorker.terminate(); });
这种方法适合对定时器精度要求极高的场景,但需要额外维护Worker文件,现代浏览器均支持该特性。
内容的提问来源于stack exchange,提问作者AnthonyDa
相关产品推荐
相关产品推荐

