浏览器页面A中初始化定时器,切换到页面B后会执行一段时间然后暂停,返回页面A后定时器会继续执行。
记当前页面为A,通过script引入一个含有定时器的脚本。
可以看到,页面A脚本加载完成后,初始化了一个setInterval ,并开始定时执行。
但是当切换到页面B后,定时器会持续执行一段时间,然后暂停。此时若切换回A,定时器会立刻开始执行。
疑惑点在于切换页面的时候定时器出现了暂停的效果。
当前代码环境和测试结果:
1、切换页面不牵扯到任何 对focus blur unload的处理,也不曾监听页面状态变化。
2、clearInterval 会直接清除定时器,而且在切换页面过程中不曾重新初始化定时器,达不到当前暂停的效果。
3、如果重新写个简单的页面对 setInterval进行测试,在切换页面的时候定时器并不会暂停。
甚至在定时器暂停之后直接控制台添加定时器,也不会执行
在切换浏览器页面时,设置的 setInterval 会暂停执行是因为 JavaScript 是单线程执行的,在切换浏览器页面时 JavaScript 引擎会停止运行,导致定时器事件不能继续执行。解决方案可以考虑使用 setTimeout 代替 setInterval,结合计数器进行误差修正,尽量让误差缩小,不同浏览器不同时间段打开页面倒计时误差可控制在 1 秒以内。具体实现步骤如下:
修改原有的 setInterval 实现为 setTimeout,同时引入计数器 count 和变量 start 记录倒计时开始时间。
设置 setTimeout 回调函数 countDownStart(),在该回调函数内对误差进行修正并调用 setTimeout,实现递归定时调用。
在 setTimeout 回调函数内计算误差 offset 和下一次执行的时间 nextTime,并更新计数器 count 和倒计时数值 countDownNum,同时在控制台输出倒计时误差时间和下一次执行时间信息。
根据倒计时数值 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);
}
}
}