Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >如何让定时器在页面最小化的时候不执行?

如何让定时器在页面最小化的时候不执行?

作者头像
GopalFeng
发布于 2022-08-01 12:10:25
发布于 2022-08-01 12:10:25
1.8K00
代码可运行
举报
运行总次数:0
代码可运行

本文是深入浅出 ahooks 源码系列文章的第七篇,这个系列的目标主要有以下几点:

  • 加深对 React hooks 的理解。
  • 学习如何抽象自定义 hooks。构建属于自己的 React hooks 工具库。
  • 培养阅读学习源码的习惯,工具库是一个对源码阅读不错的选择。

注:本系列对 ahooks 的源码解析是基于 v3.3.13。自己 folk 了一份源码,主要是对源码做了一些解读,可见 详情[1]

今天我们来聊聊定时器。

useInterval 和 useTimeout

看名称,我们就能大概知道,它们的功能对应的是 setInterval 和 setTimeout,那对比后者有什么优势?

先看 useInterval,代码简单,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 跟上面很类似,如下所示,不再做额外解释:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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,并不会立即执行。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 类似,这里不展开细说)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

直接上代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

清除定时器。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端杂货铺 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
ahooks 是怎么解决 React 的闭包问题的?
本文是深入浅出 ahooks 源码系列文章的第三篇,这个系列的目标主要有以下几点:
GopalFeng
2022/08/01
1.4K0
ahooks 是怎么解决 React 的闭包问题的?
JavaScript设置定时器、取消定时器及执行机制解析
今天整理了一下 JavaScript 定时器,顺便了解了一下 JavaScript 的运行机制,现在记录一下。
德顺
2019/11/12
5.2K0
记录第一次给开源项目提 PR
本文是深入浅出 ahooks 源码系列文章的第八篇,这个系列的目标主要有以下几点:
GopalFeng
2022/08/01
7150
记录第一次给开源项目提 PR
JavaScript定时器与执行机制详细介绍
由于JS是单线程,所以同一时间只能执行一个任务,其他任务就得排队,后续任务必须等到前一个任务结束才能开始执行。
Javanx
2019/09/05
1.3K0
JavaScript定时器与执行机制详细介绍
ahooks 中那些控制“时机”的hook都是怎么实现的?
本文是深入浅出 ahooks 源码系列文章的第五篇,这个系列的目标主要有以下几点:
GopalFeng
2022/08/01
1.6K0
ahooks 中那些控制“时机”的hook都是怎么实现的?
落魄前端,整理给自己的前端知识体系复习大纲(下篇)
也许内容并不深入,因为文章的标题与定义就是大纲,并没指望一篇文章,可以教会提到的对应的知识点。无论哪个知识点,一个原型链最基础的东西,都可进行深挖,大神都可以扯上几万字,也未必能让所有人看懂,培训机构上万的学费,也未必敢说一句包你懂,何况笔者只是一个免费的大纲汇总呢?本文仅是大纲而已。大家哪个知识点不熟悉,应该学会自己查漏补缺,自己学习着去深入。
用户8671053
2021/09/19
6130
Note·React Hook 定时器
前段时间学习了 React 新发布的 Hook 新功能,学完立马就爱上了这个新 API,用起来感觉比 class 组件爽多了。但 hook 虽然看似简单,但是要能熟练运用还是得花上一段时间。
数媒派
2022/12/01
6290
JS深入浅出 - requestAnimationFrame
早期 JS 定时动画:主要通过 setTimeout 和 setIntarval 实现。 HTML5 出现后:又出现了两种实现动画的方式,1. CSS 动画(transition、animation)2. H5的 canvas 实现。 与此同时,HTML5 还提供了一个专门用于请求动画的 API requesetAniamtionFrame(),统一了 DOM 动画、canvas动画、svg动画、webGL动画等的刷新机制。
Qwe7
2022/03/28
2.1K0
requestAnimationFrame,终结定时器动画时代!
风和日丽,饶有兴致,翻开之前写的一个简单的动画插件,发现是用定时器写的,但是作为有追求的前端,一个问题怎么能有一种解决方案呢?故而,遍寻资料,终于看见曙光,让我查到了requestAnimationFrame 这个宿主对象的方法,也能能优雅的实现js动画!
用户7413032
2020/06/11
1.6K0
requestAnimationFrame,终结定时器动画时代!
如何封装 cookie/localStorage/sessionStorage hook?
本文是深入浅出 ahooks 源码系列文章的第九篇,这个系列的目标主要有以下几点:
GopalFeng
2022/08/01
1.3K0
requestAnimationFrame实现动画效果
html动画一般会采用css3的形式去做,当然也比较建议用css去做动画。但是有时候一些动画只能使用js来完成,常用的js动画方案是使用计时器来完成。编写动画循环的一个关键在于我们要延迟多长时间比较合适,如何设置时间才能让动画显得平滑顺畅,这个时间并不是越小越好,因为要遛狗足够的时间间隔来让浏览器产生渲染变化,否则就会变成跳跃感。
OECOM
2020/07/02
2.2K0
JavaScript基础学习--10 return、定时器基础
Demos:   https://github.com/jiangheyan/JavaScriptBase 一、return      1、返回值:数字、字符串、布尔、函数、对象({}、[]、元素、null)、未定义      2、return值==fn(); function fn(a){ return function (b){ alert(a+b); } } alert(fn()) //function (b){ ale
用户1148399
2018/01/09
8660
js动画requestAnimationFrame详解「建议收藏」
requestAnimationFrame() 他的作用就是代替定时器做更加流畅高性能的动画,做可以匹配设备刷新率的动画,他解决了定时器做动画时间间隔不稳定的问题(也就是解决定时器做动画不流畅的问题)。他的用法与setTimeout差不多。
全栈程序员站长
2022/09/27
11K0
如何实现比 setTimeout 快 80 倍的定时器?
很多人都知道,setTimeout 是有最小延迟时间的,根据 MDN 文档 setTimeout:实际延时比设定值更久的原因:最小延迟时间[1] 中所说:
winty
2021/05/18
1.2K0
如何实现比 setTimeout 快 80 倍的定时器?
从根上理解 React Hooks 的闭包陷阱(续集)
上篇文章我们知道了什么是 hooks 的闭包陷阱,它的产生原因和解决方式,并通过一个案例做了演示。
神说要有光zxg
2022/06/06
9860
从根上理解 React Hooks 的闭包陷阱(续集)
详解 JS 中的事件循环、宏/微任务、Primise对象、定时器函数,以及其在工作中的应用和注意事项
当时的我年轻气盛,在简历上放了自己的博客地址,而面试官应该是翻了我的博客,好几道面试题都是围绕着我的博文来提问
用户6256742
2024/06/22
7900
# JavaScript 专题之 This 和定时器
思维导图:https://naotu.baidu.com/file/7d05ddb397c649f62136040993cbd04bopen in new window
九旬
2023/10/18
2450
# JavaScript 专题之 This 和定时器
前端一面常见面试题及答案_2023-02-27
(2)HTTP劫持: (访问⾕歌但是⼀直有贪玩蓝⽉的⼴告),由于http明⽂传输,运营商会修改你的http响应内容(即加⼴告)
用户10377376
2023/02/27
4890
三种自定义 hook 的事件封装方式,你会选择哪种?
我们经常通过自定义 hook 的方式抽离组件的逻辑,而这种自定义 hook 里很多都是给元素绑定事件的。
神说要有光zxg
2024/04/19
3410
三种自定义 hook 的事件封装方式,你会选择哪种?
React-Hooks怎样封装防抖和节流-面试真题
防抖函数,我们需要做的是在一件事触发的时候设置一个定时器使事件延迟发生,在定时器期间事件再次触发的话则清除重置定时器,直到定时器到时仍不被清除,事件才真正发生。
beifeng1996
2022/10/05
5850
推荐阅读
相关推荐
ahooks 是怎么解决 React 的闭包问题的?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验