Javascript 防抖 节流 控制单位时间内方法执行次数
2022-08-19
阅读 {{counts.readCount}}
评论 {{counts.commentCount}}
<br><br>
## 前言
和以往不同,没什么原创内容,就是一篇纯笔记,防止自己以后忘
全部来源于学习b站up主
[前端小夏老师](https://space.bilibili.com/8999778)
的这个视频
[函数的防抖和节流 -- JS 原生面试题](https://www.bilibili.com/video/BV1Vy4y1y7tj)
简单解释下防抖节流
都是在单位时间内,限制 `function` 被执行的次数
* 节流好理解,就是点完一次,停止接待1秒,然后在继续接待,停止过程中你随便点,有反应算我输。
* 防抖也不复杂,和上面类似,区别是,必须停够1秒才能再点。如果遇到一直不停的点,那就一直不重置,必须老实停1秒不点,才恢复,和我起床一样,越催越起不来。
<br>
应用范围很广
包括不限于
用户夏姬八乱点按钮(主要是这个)
监听鼠标滚轮(忘了方法名叫啥了)
`windows.onresize`
反复提交订单
反复发验证码之类的
等等
<br><br>
## 代码
防抖
```javascript
/**
* 防抖核心代码
* 逻辑是
* 第一次点完以后开始计时
* 如果单位时间内点了的话
* 不但无效,而且时间从头计算
*
* 例如
* 设置3000毫秒
* 第一次点完如果3秒内再次点击
* 不但不触发任何效果,而且3秒从头开始算
* 如果一直点一直点,就永远无效
*/
function debounce(fn, timer) {
let timeout = null;
return function () {
if (timeout) {
clearTimeout(timeout);
} else {
fn.apply(this, arguments);
}
timeout = setTimeout(() => {
timeout = null;
}, timer);
}
}
```
节流
```javascript
/**
* 节流核心代码
* 逻辑是
* 第一次点完以后开始计时
* 如果下次点击时间没达到设定时间
* 则无效,否则有效
*
* 例如
* 设置3000毫秒
* 第一次点击完过了1秒点击 判定为无效 立刻记录当前时间
* 过了2秒点击 与上一次时间对比 不足3秒 判定无效
* 过了3秒以上点击 与上次时间对比 足够3秒 判定有效 并立刻记录当前时间
*
*/
function throttle(fn, delay) {
let begin = 0;
return function () {
const current = new Date().getTime();
if (current - begin > delay) {
fn.apply(this, arguments);
begin = current;
}
}
}
```
<br><br>
## END
在线测试效果地址
https://tczmh.gitee.io/debounce-and-throttle/
<br><br>
## 补充1
`2022/08/23` 关于用法和其他需求的补充
首先是用法问题
```html
<!-- 这段代码中节流是无法触发的 -->
<button onclick="throttle(click0, 1000)">按钮</button>
<!-- 而这段代码中就可以触发 -->
btn.addEventListener('click', throttle(click0, 1000), false);
<!--
根据我的测试,不推荐放到onclick中触发,如果一定要这样写,就2个方法解决
要么,改写throttle方法,去除return function等,返回值搬到方法里,begin放到函数外面
要么,如下代码申明一个变量,放到onclick中
-->
<button onclick="click">按钮</button>
const click = throttle(() => {
sp0.innerText = ++c0
}, 1000);
```
<br>
另外关于 `onresize`
我之前用过echarts,他默认 `onresize` 会延迟执行,
我需求类似echarts `onresize` 的时候重新渲染canvas、
<br>
如果如果直接把渲染写到 `onresize` 中,会出现改变窗口大小,会高频反复触发,导致性能浪费卡顿,需要限制触发频率,但是和节流和防抖的需求都不一样。比如你拉窗口大小,`onresize` 一共触发300次,希望是执行其中的10次,但最后一次必然要触发,否则渲染的画面就不对了少掉最后一次。
<br>
遇到的问题是,无论是debounce还是throttle
都是减少了执行次数,而不去执行最后一次
也就是窗口变化后,只有第一次和中间某几次,会执行,最终窗口定格了,是最需要执行的,反而不执行了。。。
<br>
解决方法是再写一个ondelay方法
个人觉得会更合适onresize的需求
```javascript
// 就是延迟300秒执行,如果300秒内重复触发,则重置时间,必须停满300秒才执行
function delay(fn, delay) {
let timeout = null;
return function () {
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, delay);
}
}
// 调用, 这里的resize是你需要延迟执行的function
window.onresize = utils.delay(resize, 300);
```
<br>
<br>
另外关于 `VUE` 中的防抖和节流问题,下一篇再研究吧
今天累了不想打字了