页面计时器不准确,怎么解决?

倒计时为啥不准

先说原因:JavaScript是单线程,setTimeout (主线程空闲时才会去执行任务)的回调函数会被放入事件队列,既然要排队,就可能被前面的任务阻塞导致延迟 。且任务本身从call stack(执行栈)中拿出来执行也要耗时。所以有1000变1002也合理。就算setTimeout的第二个参数设为0,也会有至少有4ms的延迟。

javascript 复制代码
let total = 9  // 倒计时9s
    let preStamp = Date.now();
    let nowStamp = preStamp;
    const countDown = ()=>{
        if(total > 0){
          preStamp = nowStamp
          nowStamp = Date.now();
          total--
          console.log('最后时间:',new Date().toLocaleString(),'总共耗时:', nowStamp - preStamp)
          setTimeout(countDown ,1000)
        }
      }
      countDown()

正常情况下,浏览器tab页是激活或者显示状态下:

有几毫秒的误差也正常,毕竟setTimeout是先把任务放到任务队列里,等主线程空闲时才会去执行。

但是有一种情况,就是tab页或者页面不显示或者隐藏时,倒计时函数的误差就会非常大:

原因:当页面处于后台时,浏览器会降低定时器的执行频率以节省资源,导致 setTimeout 的延迟增加。切回来后又正常了

解决办法

1. 监听 visibilitychange 事件,在切回tab时修正(不推荐)

javascript 复制代码
let timer:any = null;
    let total = 20  // 倒计时9s
    let preStamp = Date.now();
    let nowStamp = preStamp;
    useEffect(() => {
        const handleVisibilityChange = () => {
          console.log('Page is visible:', document.visibilityState);
          if(document.visibilityState === 'visible'){
            preStamp = Date.now();
            nowStamp = preStamp;
            countDown()
          }else{
            timer && clearTimeout(timer)
          }
        };

        // 添加事件监听器
        document.addEventListener('visibilitychange', handleVisibilityChange);
        // 清理函数:移除事件监听器
        return () => {
            document.removeEventListener('visibilitychange', handleVisibilityChange);
        };
    })
    const countDown = ()=>{
        if(total > 0){
          preStamp = nowStamp
          nowStamp = Date.now();
          total--
          nowStamp - preStamp && console.log('最后时间:',new Date().toLocaleString(),'总共耗时:', nowStamp - preStamp)
          timer = setTimeout(countDown ,1000)
        }
      }
      countDown()

当前代码是,tab隐藏的时候停掉倒计时,可以保证页面显示的时候倒计时还是每一秒执行一次(这种情况只能用来控制动画,如果是有特殊操作,就不能用这个方法了)。有的博主可能是在tab隐藏时调整倒计时的频次,就是setTimeout的第二个参数,但是我尝试了,不停掉计时器,倒计时会一直跑,而且,隐藏的tab页面时间越久,误差会越大,没有任何意思!

2. web worker(推荐)

复制代码
用web worker,单独的线程去计时,不会受切tab影响
javascript 复制代码
//worker.ts
let total = 9;
let startTime = Date.now();
let preStamp = Date.now();
let nowStamp = preStamp;
const countDown = () => {
  const elapsed = Math.floor((Date.now() - startTime) / 1000);
  const remaining = total - elapsed;
  if (remaining > 0) {
    preStamp = nowStamp
    nowStamp = Date.now();
    postMessage({cutN: remaining, time: nowStamp - preStamp});
    setTimeout(countDown, 1000);
  } else {
    postMessage(0);
  }
};

countDown();
javascript 复制代码
//index.ts
  const worker = new Worker(new URL('./worker.ts', import.meta.url));
    worker.onmessage = (e) => {
      console.log('剩余时间:', e.data.cutN, '当前时间:', new Date().toLocaleString(), e.data.time);
    };

tab页面隐藏后,倒计时正常执行,准确度也很好;

//文中有不对或者不严谨的地方谢谢这正,小的看见会立刻纠正改过,重新做人!!!

相关推荐
Dream-Y.ocean2 小时前
鸿蒙PC平台宅匠 3D 室内设计适配实战:基于 Electron 壳方案的 Three.js 双渲染引擎与参数化户型编辑器开发
javascript·3d·harmonyos
Highcharts.js3 小时前
Highcharts Gantt使用符号的自定义数据标签开发甘特图示例
javascript·甘特图·highcharts·图表开发·实战代码·响应式图表
kyriewen6 小时前
Skills 榜前 10 我全装了:只有 3 个能直接用
前端·javascript·程序员
工控丫丫6 小时前
帆软看板中经常使用的公式函数及经常搭的组合
前端·javascript·数据库
Dream-Y.ocean9 小时前
鸿蒙PC平台星图遥感适配实战:基于 Electron 壳方案的纯 JavaScript 遥感图像处理引擎开发
javascript·electron·harmonyos
螺丝钉tina9 小时前
[Playwright ][16]Playwright + TypeScript API 自动化测试实战:API测试
前端·javascript·typescript
Highcharts.js12 小时前
React 中原生支持使用 JSX 组件配置自定义表格 Highcharts Grid
前端·javascript·react.js·前端框架·jsx·highcharts·自定义表格
Java后端的Ai之路12 小时前
【AIAgent开发】-Agent工具封装成CLI(纯干货!!!)
java·javascript·人工智能·agent·cli
薛定谔的猫-菜鸟程序员12 小时前
Electron开发超详细教程(从入门到放弃)
前端·javascript·electron