踩坑日记 8 - tabs 切换后定时器变慢

问题:

切换 tabs 到后台、最小化浏览器,定时器变慢

原因:

浏览器的 Timer throttling 策略:为了提高性能和电池寿命、减少资源的占用,Chrome 浏览器对于未激活的标签页,定时器的最小间隔时间会变为每秒不超过一次。Chrome 88 后更新了更为激进的省电策略,导致后台非活动页面中 setInterval、setTimeout 回调的执行间隔拉长到 1 分钟以上。

解决方法:

  1. 使用 Web Worker
  2. HackTimer:基于 Web Worker,模拟 settimeout、setInterval
  3. 监听 visibilitychange,判断 tab 是否隐藏,记录失活时间后更新,频繁切换会丢失精度。线上 demo:标签未激活状态 setInterval/setTimeout 出现变慢问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// html 部分
<span>本站已安全运行:</span>
<span id="timeDate">载入天数...</span>
<span id="times">载入时分秒...</span>
// js 部分
<script>
let count = 0 // 失活 激活 时间差
let now = new Date();
let addDate = new Date(count)
function createtime() {
let grt = new Date("2021-05-28 22:10:58"); //在此处修改你的建站时间
if(count>0){
now.setTime(now.getTime() + count + 250);
count = 0
}else{
now.setTime(now.getTime() + 250);
}
days = (now - grt) / 1000 / 60 / 60 / 24;
dnum = Math.floor(days);
hours = (now - grt) / 1000 / 60 / 60 - (24 * dnum);
hnum = Math.floor(hours);
if (String(hnum).length == 1) {
hnum = "0" + hnum;
}
minutes = (now - grt) / 1000 / 60 - (24 * 60 * dnum) - (60 * hnum);
mnum = Math.floor(minutes);
if (String(mnum).length == 1) {
mnum = "0" + mnum;
}
seconds = (now - grt) / 1000 - (24 * 60 * 60 * dnum) - (60 * 60 * hnum) - (60 * mnum);
snum = Math.round(seconds);
if (String(snum).length == 1) {
snum = "0" + snum;
}
document.getElementById("timeDate").innerHTML = dnum + " 天 ";
document.getElementById("times").innerHTML = hnum + " 小时 " + mnum + " 分 " + snum + " 秒";
}


let timer = setInterval("createtime()", 250);

function handleVisibilityChange() {
if (document.visibilityState == 'hidden') {
console.log('visibilitychange - hidden');
clearInterval(timer); //为了兼容pc,pc切换到后台继续运行
start = new Date().getTime(); // 记录tab页失活开始时间
} else if (document.visibilityState == 'visible') {
end = new Date().getTime(); // 记录tab页激活开始时间
// count = Math.floor((end - start) / 1000); // 计算时间差 s
count = end - start
timer = setInterval("createtime()", 250);
console.log('visibilitychange - visible',start,end);
}
}

document.addEventListener('visibilitychange', handleVisibilityChange);
</script>

参考文章:

Web Worker
Timer throttling 策略
chrome57 - backgroundTabs
Heavy throttling of chained JS timers beginning in Chrome 88