背景
在项目中,使用了setInterval()定时器函数实现一个倒计时的功能,并且倒计时的时长是可变的,当时长设置过大,并且在倒计时的过程中,切换浏览器,或者最小化,当再次返回的时候会出现定时器不准或者卡住的问题;
在网上看了很多文章:
- 首先受事件循环机制的原因,定时器设置的delay时间延迟没那么准确,这个还是比较好理解的;
- 第二点就是,看到有文章说浏览器tab切换,浏览器为了优化后台tab的加载损耗(以及降低耗电量),在未被激活的tab中定时器的最小延时限制为1S(1000ms),如果延时限制小于1S,也会出现不准的状况,这个我没有去尝试,因为我设置的时间间隔是1S;
- 然后就是浏览器最小化或者切换到后台运行时,时间长了之后浏览器的休眠机制,我觉得我的问题大概率就是这个原因造成的;
解决方案
网上提供的一些解决方案:
- 使用setTimeout()实现setInterval()的机制,我觉得我的问题点不在这;
- requestAnimationFrame,这个以后有时间看一下,目前不太了解;
- Web Worker:目前使用的是这种方式;
Web Worker
Web Worker 使得在一个独立于 Web 应用程序主执行线程的后台线程中运行脚本操作成为可能。这样做的好处是可以在独立线程中执行费时的处理任务,使主线程(通常是 UI 线程)的运行不会被阻塞/放慢。
实现一个定时器的基本操作:开启,暂停,重置,清除;并且我们是因为主线程中定时器不准才使用Worker的,所以我觉得重点是在正确的时间点告诉主线程即可,具体主线程要在定时器中做什么,那就让主线程在接受到消息的时候,自己去做就好了;
timeWorker.js:
js
// 监听主线程的指令
let intervalId = null;
let isRunning = false;
let count = 0;
let interval = '';
let countDown = 0;
let isRunning = false;
self.onmessage = (e) => {
switch (e.data.action) {
case 'start':
startInterval();
break;
case 'pause':
pauseInterval();
break;
case 'stop':
clearInterval();
break;
case 'reset':
resetInterval();
break;
}
};
// 开启
const startInterval = () => {
if (!interval) {
isRunning = true;
interval = setInterval(() => {
self.postMessage({ msg: '定时器开启~', count });
}, 1000);
}
};
// 暂停
const pauseInterval = () => {
if (isRunning) {
clearInterval(interval);
isRunning = false;
}
};
// 重置
const resetInterval = () => {
clearInterval(interval);
isRunning = false;
count = 0;
startInterval();
};
// 清除
const clearInterval = () => {
clearInterval(interval);
isRunning = false;
count = 0;
};
主线程中在需要开启对定时器进行操作的地方进行即可;
js
const worker = new Worker('./timeWorker.js');
// 接收消息
worker.onmessage = (e) => {
// ...处理自己的业务逻辑
console.log(e.data);
};
// 发消息
worker.postMessage({ action: 'start' });
先简单记录一下,以后有时间再详细记录;