setInterval 会在切换页面一段时间后暂停

浏览器页面A中初始化定时器,切换到页面B后会执行一段时间然后暂停,返回页面A后定时器会继续执行。
记当前页面为A,通过script引入一个含有定时器的脚本。

img

可以看到,页面A脚本加载完成后,初始化了一个setInterval ,并开始定时执行。

img


img

但是当切换到页面B后,定时器会持续执行一段时间,然后暂停。此时若切换回A,定时器会立刻开始执行。

疑惑点在于切换页面的时候定时器出现了暂停的效果。

当前代码环境和测试结果:
1、切换页面不牵扯到任何 对focus blur unload的处理,也不曾监听页面状态变化。
2、clearInterval 会直接清除定时器,而且在切换页面过程中不曾重新初始化定时器,达不到当前暂停的效果。
3、如果重新写个简单的页面对 setInterval进行测试,在切换页面的时候定时器并不会暂停。

甚至在定时器暂停之后直接控制台添加定时器,也不会执行

img

  • 以下回答由chatgpt基于相关博客总结生成:

    在切换浏览器页面时,设置的 setInterval 会暂停执行是因为 JavaScript 是单线程执行的,在切换浏览器页面时 JavaScript 引擎会停止运行,导致定时器事件不能继续执行。解决方案可以考虑使用 setTimeout 代替 setInterval,结合计数器进行误差修正,尽量让误差缩小,不同浏览器不同时间段打开页面倒计时误差可控制在 1 秒以内。具体实现步骤如下:

    1. 修改原有的 setInterval 实现为 setTimeout,同时引入计数器 count 和变量 start 记录倒计时开始时间。

    2. 设置 setTimeout 回调函数 countDownStart(),在该回调函数内对误差进行修正并调用 setTimeout,实现递归定时调用。

    3. 在 setTimeout 回调函数内计算误差 offset 和下一次执行的时间 nextTime,并更新计数器 count 和倒计时数值 countDownNum,同时在控制台输出倒计时误差时间和下一次执行时间信息。

    4. 根据倒计时数值 countDownNum 判断是否结束执行 setTimeout,否则设置下一次调用 setTimeout。

    具体代码实现如下:

    countTime: function () {
      var that = this;
      let countDownNum = that.getCountdownTime(); //获取倒计时初始值
      if (countDownNum <= 0) return;
      var string = that.getCountdownString(countDownNum);
      that.setData({
        countdownTime: string
      });
      var interval = 1000, start = new Date().getTime(), count = 0;
      that.data.timer = setTimeout(countDownStart, interval);
      function countDownStart() {
        var offset, nextTime; // offset是倒计时误差时间,nextTime是减去误差时间后下一次执行的时间
        count++;
        offset = new Date().getTime() - (start + count * interval);
        nextTime = interval - offset;
        if (nextTime < 0) { nextTime = 0; }
        countDownNum -= interval;
        var string = that.getCountdownString(countDownNum);
        console.log("误差: " + offset + "ms, 下一次执行: " + nextTime + "ms后,离活动开始还有: " + countDownNum + "ms");
        if (countDownNum <= 0) {
          clearTimeout(that.data.timer);
        } else {
          that.setData({
            countdownTime: string
          })
          that.data.timer = setTimeout(countDownStart, nextTime);
        }
      }
    }