这是我最近开发碰到的一个问题,本文是我测试出来的实践结果,供你们参考。html
关于js定时器,setInterval
和setTimeout
,做为咱们平常开发常用到的方法,你们必定很是熟悉。好比下面一个例子:前端
setInterval(() => {
console.log('1');
}, 500);
复制代码
做为刚学前端没多久的新人也能知道,这段代码就是每过500ms打印一次1(实际运行还须要考虑js的宏任务和微任务的执行时间,定时器的间隔时间是500ms,可是定时器中的方法触发可能须要在宏任务队列中排队,不必定会在500ms的时候触发,关于Event Loop的基础内容不在本文讨论以内)。web
可是若是你把浏览器从当前页面切换到另外一个标签页,或者把浏览器最小化了,这时候,这个页面定时器的间隔时间仍是500ms?api
本文将测试setInterval
、setTimeout
、requestAnimationFrame
这三个方法在浏览器可见以及不可见状态下的表现,个人测试浏览器以及版本是谷歌(86.0.4240.193)
,火狐(81.0.2)
,ie11
。浏览器
浏览器的可见和不可见状态的切换会触发visibilitychange
事件,咱们能够经过监听这个事件来判别浏览器的可见状态。性能优化
document.addEventListener("visibilitychange", function() {
console.log(document.visibilityState);
});
复制代码
document.visibilityState
有三个值websocket
这里重点关注hidden
这个值,当咱们浏览器切换当前页面到另一个标签页或者把浏览器最小化的时候,document.visibilityState
就会是hidden
值。咱们也可使用document.hidden
,它返回一个布尔值,为true
的时候,说明当前浏览器是不可见状态。markdown
关于visibilitychange
的细节能够看阮一峰老师的这篇文章 Page Visibility API 教程。app
咱们先来测试setInterval
,代码以下socket
<button id="btn">开始计时</button>
// 兼容ie写法
document.getElementById('btn').addEventListener('click', function() {
setInterval(function() {
const myDate = new Date();
const currentDate = myDate.getMinutes() + '分'+ myDate.getSeconds() + '秒' + myDate.getMilliseconds() + '豪秒';
// 每次循环打印当前时间
console.log(currentDate);
}, 500);
});
// 浏览器可见状态切换事件
document.addEventListener('visibilitychange', function() {
if(document.hidden) {
console.log('页面不可见');
}
});
复制代码
定时器间隔是500ms,先来看下谷歌浏览器
咱们发现,当页面不可见以后,定时器的间隔变成了1s。 接下来,咱们把定时器间隔改为2s来试下。
先后间隔时间一致。
接下来测试一下火狐
和ie
。这里列出的图片都是500ms和2s的例子。
ie浏览器
通过我大量的测试,能够得出结论,谷歌浏览器中,当页面处于不可见状态时,setInterval的最小间隔时间会被限制为1s。火狐浏览器的setInterval和谷歌特性一致,可是ie浏览器没有对不可见状态时的setInterval进行性能优化,不可见先后间隔时间不变。
接下来是setTimeout
function timer() {
setTimeout(function() {
const myDate = new Date();
const currentDate = myDate.getMinutes() + '分'+ myDate.getSeconds() + '秒' + myDate.getMilliseconds() + '豪秒';
console.log(currentDate);
timer();
}, 500)
}
// 兼容ie写法
document.getElementById('btn').addEventListener('click', function() {
timer();
});
复制代码
一样先来看看在谷歌浏览器中的表现(仍是500ms和2s)
咱们发如今谷歌浏览器中,500ms的间隔,setTimeout
和setInterval
表现一致,都是最小间隔限制为1s。可是2s隔间的测试结果出现了分歧,页面不可见以后,间隔变成了3s。继续通过屡次的测试,以下,左图的间隔时间为990ms,右图的间隔时间为1s。
不可见状态下,左图中的990ms间隔时间变为1s,右图中的1s间隔时间变为2s。
咱们再来看看火狐
(500ms和2s)
火狐
浏览器不可见状态下,左图中的500ms变为1s,右图中的2s保持不变。
再来看看ie
浏览器(500ms)
同样毫无优化。
咱们能够得出结论,在谷歌浏览器中,setTimeout在浏览器不可见状态下间隔低于1s的会变为1s,大于等于1s的会变成N+1s的间隔值。火狐浏览器下setTimeout的最小间隔时间会变为1s,大于等于1s的间隔不变。ie浏览器在不可见状态先后的间隔时间不变。
raf是浏览器提供的一个更流畅的处理动画的方法,它会在下次浏览器GUI绘制页面的时候运行传入的方法。GUI绘制页面的频率跟显示器的刷新率有关,普通显示器的刷新率是60hz,所以raf在一秒以内须要运行60次,间隔四舍五入大概是17ms。
function timer() {
const myDate = new Date();
const currentDate = myDate.getMinutes() + '分'+ myDate.getSeconds() + '秒' + myDate.getMilliseconds() + '豪秒';
console.log(currentDate);
window.requestAnimationFrame(timer)
}
// 兼容ie写法
document.getElementById('btn').addEventListener('click', function() {
timer();
});
复制代码
咱们来看看不一样浏览器下面的表现:
谷歌浏览器
火狐浏览器
ie浏览器
咱们能够发现,谷歌浏览器和ie浏览器当浏览器状态为不可见时,raf方法将中止执行。火狐浏览器当状态变为不可见时,会在间隔是1s,2s,4s,8s,16s,32s...这样的顺序下去执行raf方法。
谷歌浏览器中,当页面处于不可见状态时,setInterval
的最小间隔时间会被限制为1s。火狐浏览器的setInterval
和谷歌特性一致。ie浏览器没有对不可见状态时的setInterval
进行性能优化,不可见先后间隔时间不变。
在谷歌浏览器中,setTimeout
在浏览器不可见状态下间隔低于1s的会变为1s,大于等于1s的会变成N+1s的间隔值。火狐浏览器下setTimeout
的最小间隔时间会变为1s,大于等于1s的间隔不变。ie浏览器在不可见状态先后的间隔时间不变。
谷歌浏览器和ie浏览器当浏览器状态为不可见时,raf
方法将中止执行。火狐浏览器当状态变为不可见时,会在间隔是1s,2s,4s,8s,16s,32s...这样的顺序下去执行raf
方法。
碰到问题固然须要解决,在一些定时器小于1s的倒计时的页面中,若是用户切换到了其余标签页。再切回去的时候,页面上显示的倒计时时间实际上是错误的,这种隐藏的bug会带来很大的风险。该怎么解决呢?
除了调取后台接口或者websocket
链接以外,其实有一个更好的解决方案,webWorkers
。并且webWorkers
还能够解决一个页面存在多个定时器时候间隔时间偏差较大的问题。
直接上例子
document.getElementById('btn').addEventListener('click', function() {
var w = new Worker('demo_workers.js');
w.onmessage = function(event){
console.log(event.data);
};
});
//浏览器切换事件
document.addEventListener('visibilitychange', function() {
if(document.hidden) {
console.log('页面不可见');
}
});
复制代码
// demo_workers.js
setInterval(function() {
const myDate = new Date();
const currentDate = myDate.getMinutes() + '分'+ myDate.getSeconds() + '秒' + myDate.getMilliseconds() + '豪秒';
postMessage(currentDate);
}, 500);
复制代码
实际结果
间隔保持一致。
若是本文有帮助到你的地方,请帮忙点个赞,感谢支持。