当前位置:网站首页>如何让定时器在页面最小化的时候不执行?
如何让定时器在页面最小化的时候不执行?
2022-08-01 20:58:00 【GopalFeng】
本文是深入浅出 ahooks 源码系列文章的第七篇,这个系列的目标主要有以下几点:
- 加深对 React hooks 的理解。
- 学习如何抽象自定义 hooks。构建属于自己的 React hooks 工具库。
- 培养阅读学习源码的习惯,工具库是一个对源码阅读不错的选择。
注:本系列对 ahooks 的源码解析是基于 v3.3.13
。自己 folk 了一份源码,主要是对源码做了一些解读,可见 详情[1]。
今天我们来聊聊定时器。
useInterval 和 useTimeout
看名称,我们就能大概知道,它们的功能对应的是 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 的区别如下:
- 可以支持第三个参数,通过 immediate 能够立即执行我们的定时器。
- 在变更 delay 的时候,会自动清除旧的定时器,并同时启动新的定时器。
- 通过 useEffect 的返回清除机制,开发者不需要关注清除定时器的逻辑,避免内存泄露问题。这点是很多开发者会忽略的点。
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 和 setInterval 作为事件循环中宏任务的“两大主力”,它的执行时机不能跟我们预期一样准确的,它需要等待前面任务的执行。比如下面的 setTimeout 的第二个参数设置为 0,并不会立即执行。
setTimeout(() => {
console.log('test');
}, 0)
另外还有一种情况,setTimeout 和 setInterval 在浏览器不可见的时候(比如最小化的时候),不同的浏览器中设置不同的时间间隔的时候,其表现不一样。根据 当浏览器切换到其他标签页或者最小化时,你的js定时器还准时吗?[2] 这篇文章的实践结论如下:
谷歌浏览器中,当页面处于不可见状态时,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
。(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
。
setRafInterval
直接上代码:
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 函数。更新开始时间。
clearRafInterval
清除定时器。
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
进行实现。
系列文章:
- 大家都能看得懂的源码(一)ahooks 整体架构篇[3]
- 如何使用插件化机制优雅的封装你的请求hook [4]
- ahooks 是怎么解决 React 的闭包问题的?[5]
- ahooks 是怎么解决用户多次提交问题?[6]
- ahooks 中那些控制“时机”的hook都是怎么实现的?[7]
- 如何让 useEffect 支持 async...await?[8]
参考资料
[1]详情: https://github.com/GpingFeng/hooks
[2]当浏览器切换到其他标签页或者最小化时,你的js定时器还准时吗?: https://juejin.cn/post/6899796711401586695#comment
[3]大家都能看得懂的源码(一)ahooks 整体架构篇: https://juejin.cn/post/7105396478268407815
[4]如何使用插件化机制优雅的封装你的请求hook : https://juejin.cn/post/7105733829972721677
[5]ahooks 是怎么解决 React 的闭包问题的?: https://juejin.cn/post/7106061970184339464
[6]ahooks 是怎么解决用户多次提交问题?: https://juejin.cn/post/7106461530232717326
[7]ahooks 中那些控制“时机”的hook都是怎么实现的?: https://juejin.cn/post/7107189225509879838
[8]如何让 useEffect 支持 async...await?: https://juejin.cn/post/7108675095958126629
边栏推荐
- 使用百度EasyDL实现厂区工人抽烟行为识别
- 数字孪生北京故宫,元宇宙推进旅游业进程
- Internet使用的网络协议是什么
- New graduate students, great experience in reading English literature, worthy of your collection
- OSG Notes: Set DO_NOT_COMPUTE_NEAR_FAR to manually calculate far and near planes
- MongoDB快速上手
- 网红驼背矫正产品真的管用吗?如何预防驼背?医生说要这样做
- 函数(二)
- 【个人作品】无线网络图传模块
- 【个人作品】记之-串口日志记录工具
猜你喜欢
Interview assault 70: what is the glue bag and a bag?How to solve?
STAHL触摸屏维修一体机显示屏ET-316-TX-TFT常见故障
响应式织梦模板清洁服务类网站
使用百度EasyDL实现厂区工人抽烟行为识别
[Personal work] Wireless network image transmission module
MySQL语法基础
Internet使用的网络协议是什么
【Social Media Marketing】How to know if your WhatsApp is blocked?
【无标题】
30+的女性测试人面试经验分享
随机推荐
LinkedList源码分享
线程池处理异常的方法
[Energy Conservation Institute] Application of Intelligent Control Device in High Voltage Switchgear
The Internet giant development process
Wildcard SSL/TLS certificate
织梦发布文章提示body has not allow words错误
织梦通过数据库查询调用当前文章的留言
【Kaggle】Classify Leaves
WhatsApp group sending actual combat sharing - WhatsApp Business API account
myid file is missing
Interview assault 70: what is the glue bag and a bag?How to solve?
Redis does check-in statistics
自定义指令,获取焦点
宝塔搭建PESCMS-Ticket开源客服工单系统源码实测
Addition, Subtraction, Multiplication of Large Integers, Multiplication and Division of Large Integers and Ordinary Integers
【Untitled】
SIPp installation and use
win10版本1803无法升级1903系统如何解决
Buttons with good user experience should not have hover state on mobile phones
Excel advanced drawing techniques, 100 (22) - how to respectively the irregular data