当页面最小化时定时器不执行怎么办
Admin 2022-08-09 群英技术资讯 790 次浏览
注:本系列对 ahooks 的源码解析是基于 v3.3.13
。自己 folk 了一份源码,主要是对源码做了一些解读,可见 详情。
今天我们来聊聊定时器。
看名称,我们就能大概知道,它们的功能对应的是 setInterval 和 setTimeout,那对比后者有什么优势?
先看 useInterval
,代码简单,如下所示:
function useInterval( fn: () => void, delay: number | undefined, options?: { immediate?: boolean; }, ) { const immediate = options?.immediate; const fnRef = useLatest(fn); useEffect(() => { // 忽略部分代码... // 立即执行 if (immediate) { fnRef.current(); } const timer = setInterval(() => { fnRef.current(); }, delay); // 清除定时器 return () => { clearInterval(timer); }; // 动态修改 delay 以实现定时器间隔变化与暂停。 }, [delay]); }
跟 setInterval 的区别如下:
useTimeout 跟上面很类似,如下所示,不再做额外解释:
function useTimeout(fn: () => void, delay: number | undefined): void { const fnRef = useLatest(fn); useEffect(() => { // ...忽略部分代码 const timer = setTimeout(() => { fnRef.current(); }, delay); return () => { clearTimeout(timer); }; // 动态修改 delay 以实现定时器间隔变化与暂停。 }, [delay]); }
首先,setTimeout 和 setInterval 作为事件循环中宏任务的“两大主力”,它的执行时机不能跟我们预期一样准确的,它需要等待前面任务的执行。比如下面的 setTimeout 的第二个参数设置为 0,并不会立即执行。
setTimeout(() => { console.log('test'); }, 0)
另外还有一种情况,setTimeout 和 setInterval 在浏览器不可见的时候(比如最小化的时候),不同的浏览器中设置不同的时间间隔的时候,其表现不一样。根据 当浏览器切换到其他标签页或者最小化时,你的js定时器还准时吗? 这篇文章的实践结论如下:
谷歌浏览器中,当页面处于不可见状态时,setInterval 的最小间隔时间会被限制为 1s。火狐浏览器的 setInterval 和谷歌特性一致,但是 ie 浏览器没有对不可见状态时的 setInterval 进行性能优化,不可见前后间隔时间不变。
在谷歌浏览器中,setTimeout在浏览器不可见状态下间隔低于1s的会变为1s,大于等于1s的会变成N+1s的间隔值。火狐浏览器下setTimeout的最小间隔时间会变为1s,大于等于1s的间隔不变。ie浏览器在不可见状态前后的间隔时间不变。
这个结论,我没有验证过,但看起来差异挺大,其中还提到了另外一个选择,就是 requestAnimationFrame。
window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
为了提高性能和电池寿命,因此在大多数浏览器里,当requestAnimationFrame() 运行在后台标签页或者隐藏的 <iframe>
里时,requestAnimationFrame() 会被暂停调用以提升性能和电池寿命。
所以,ahooks 也提供了使用 requestAnimationFrame
进行模拟定时器处理的 hook,我们一起来看下。
直接看 useRafInterval
。(useRafTimeout 和 useRafInterval 类似,这里不展开细说)。
function useRafInterval( fn: () => void, delay: number | undefined, options?: { immediate?: boolean; }, ) { const immediate = options?.immediate; const fnRef = useLatest(fn); useEffect(() => { // 省略部分代码... const timer = setRafInterval(() => { fnRef.current(); }, delay); return () => { clearRafInterval(timer); }; }, [delay]); }
可以看到,跟前面的 useInterval 大部分代码逻辑都是一样的,只是定时使用了 setRafInterval
方法,清除定时器用了 clearRafInterval
。
直接上代码:
const setRafInterval = function (callback: () => void, delay: number = 0): Handle { if (typeof requestAnimationFrame === typeof undefined) { // 如果不支持,还是使用 setInterval return { id: setInterval(callback, delay), }; } // 开始时间 let start = new Date().getTime(); const handle: Handle = { id: 0, }; const loop = () => { const current = new Date().getTime(); // 当前时间 - 开始时间,大于设置的间隔,则执行,并重置开始时间 if (current - start >= delay) { callback(); start = new Date().getTime(); } handle.id = requestAnimationFrame(loop); }; handle.id = requestAnimationFrame(loop); return handle; };
首先是用 typeof 判断进行兼容逻辑处理,假如不兼容,则兜底使用 setInterval。
初始记录一个 start 的时间。
在 requestAnimationFrame 回调中,判断现在的时间减去开始时间有没有达到间隔,假如达到则执行我们的 callback 函数。更新开始时间。
清除定时器。
function cancelAnimationFrameIsNotDefined(t: any): t is NodeJS.Timer { return typeof cancelAnimationFrame === typeof undefined; } // 清除定时器 const clearRafInterval = function (handle: Handle) { if (cancelAnimationFrameIsNotDefined(handle.id)) { return clearInterval(handle.id); } cancelAnimationFrame(handle.id); };
假如不支持 cancelAnimationFrame
API,则通过 clearInterval 清除,支持则直接使用 cancelAnimationFrame 清除。
关于定时器,我们平时用得不少,但经常有同学容易忘记清除定时器,结合 useEffect
返回清除副作用函数这个特性,我们可以将这类逻辑一起封装到 hook 中,让开发者使用更加方便。
另外,假如希望在页面不可见的时候,不执行定时器,可以选择 useRafInterval 和 useRafTimeout,其内部是使用 requestAnimationFrame
进行实现。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
这篇文章主要介绍了利用 Chrome Dev Tools 进行页面性能分析的步骤说明(前端性能优化),本文给大家介绍的非常想详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
什么是 React 高阶组件?聊聊怎么使用React高阶组件创建一个面包屑导航,希望对大家有所帮助!
阅读本文,你将学会几个超级超级实用的 响应头,解决你工作中遇到的问题;不仅解决问题,还能让你在 和后端、运维撕逼时 占据上风。
这篇文章给大家分享的是uniapp中全局变量的五种实现方式,其中一些细节知识参考uView官网。下面就跟随小编一起来看看uniapp中全局变量的实现示例吧。
这篇文章主要为大家介绍了如何编写Vue插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008