前言
很多的问题就在实践中得到解决。
本文主要说的就是js定时器,setInterval和setTimeout,作为我们日常开发经常使用到的方法。我们先给大家下面一个例子:
setInterval(() => { console.log('1'); }, 500);
这段代码就是每过500ms打印一次1(实际运行还需要考虑js的宏任务和微任务的执行时间,定时器的间隔时间是500ms,但是定时器中的方法触发可能需要在宏任务队列中排队,不一定会在500ms的时候触发,关于Event Loop的基础内容不在本文讨论之内)。
但要是从当前页面切换到另一个标签页,或者把浏览器最小化了,这时候,这个页面定时器的间隔时间还是500ms?
本文将测试setInterval、setTimeout、requestAnimationFrame这三个方法在浏览器可见以及不可见状态下的表现,我的测试浏览器以及版本是谷歌(86.0.4240.193),火狐(81.0.2),ie11。
浏览器可见和不可见状态
我要知道浏览器的可见和不可见状态的切换会触发visibilitychange事件,这样就可以通过监听这个事件来判别浏览器的可见状态。
document.addEventListener("visibilitychange", function() { console.log(document.visibilityState); });
document.visibilityState有三个值
hidden:页面彻底不可见。
visible:页面至少一部分可见。
prerender:页面即将或正在渲染,处于不可见状态。要知道重点关注hidden,主要就是它可以在浏览器切换当前页面到另外一个标签页或者把浏览器最小化的时候,document.visibilityState就会是hidden值。由此可以使用document.hidden,它返回一个布尔值,为true的时候,说明当前浏览器是不可见状态。
setInterval
我们先来测试setInterval,代码如下
<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
接下来是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浏览器在不可见状态前后的间隔时间不变。
requestAnimationFrame
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);
实际结果
间隔保持一致。
很多问题看似在“吹毛求疵”,但何尝不是一种进步。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/128192.html
我们讲述的是关于 ahooks 源码系列文章的第七篇,总结主要讲述下面几点: 巩固 React hooks 的理解。 学习如何抽象自定义 hooks。构建属于自己的 React hooks 工具库。 培养阅读学习源码的习惯,工具库是一个对源码阅读不错的选择。 注:本系列对 ahooks 的源码解析是基于v3.3.13。自己 folk 了一份源码,主要是对源码做了一些解读,可见详情。 ...
摘要:欢迎访问我的个人博客前言在工作中我们可能会遇到这样的需求,当浏览器切换到别的标签页或着最小化时,我们需要暂停页面上正在播放的视频或者音乐,这个需求就会用到我下面要说的这个知识点具体用法浏览器标签页隐藏或者显示时会改变和的值,我们可以通过这个 欢迎访问我的个人博客:http://www.xiaolongwu.cn 前言 在工作中我们可能会遇到这样的需求,当浏览器切换到别的标签页或着最小化...
摘要:如果看完本文后,还对进程线程傻傻分不清,不清楚浏览器多进程浏览器内核多线程单线程运行机制的区别。因此准备梳理这块知识点,结合已有的认知,基于网上的大量参考资料,从浏览器多进程到单线程,将引擎的运行机制系统的梳理一遍。 前言 见解有限,如有描述不当之处,请帮忙及时指出,如有错误,会及时修正。 ----------超长文+多图预警,需要花费不少时间。---------- 如果看完本文后,还...
阅读 550·2023-03-27 18:33
阅读 739·2023-03-26 17:27
阅读 633·2023-03-26 17:14
阅读 593·2023-03-17 21:13
阅读 524·2023-03-17 08:28
阅读 1804·2023-02-27 22:32
阅读 1295·2023-02-27 22:27
阅读 2183·2023-01-20 08:28