前言
最近逛掘金时,看到了一篇文章。发现是我之前写过的一篇文章主题是防抖和节流的 ,看防抖时没感觉哪里不一样,但是当我看到节流时发现他的节流怎么这么繁琐(・∀・(・∀・(・∀・*)?
抱着疑惑的想法,我仔细拜读了这篇文章。嗯。。好家伙不得不说大佬就是大佬,考虑的确实真的很细。里面的一些思想真的值得学习。今天就分享一下学习心得。代码我会贴在文章末尾。
为什么我们需要节流和防抖
举个栗子,你每次使用某度的搜索框,你每写一个字都会给你展示出相应的提示栏,类似下面这样
本质是通过监听input框的输入来通过网络请求向后端拿取数据的。乍看一眼没啥问题,但是监听input框的输入是只要进行了键盘敲击(敲击一个字母都算)就会触发回调函数向后端接口进行网络请求,一个人可能没啥事。但是百度多少用户,同时敲击直接给后台干冒烟了( ˶ˆ ᗜ ˆ˵ ),防抖和节流就是为了避免这种事情的发生。
防抖
防抖(Debounce)是一种控制函数执行频率的策略。防抖的主要目的是确保在一系列连续的调用中 ,只有当最后一次调用后的指定时间间隔内没有新的调用发生 ,该函数才会被执行一次。 那我们如何实现防抖呢?
setTimeout的作用
防抖的核心在与连续的调用中只执行最后一次 ,这意味着除了最后一次的执行前面的函数的调用都必须取消 ,但是函数如果调用了的话,js引擎会立即执行 。所以控制函数的调用我们必须借用setTimeout
,setTimeout可以延缓函数的调用 ,这意味着我们可以在延缓的时间内进行判断,如果函数再次被调用了的话,就取消前面的那一次函数调用。直到最后一次函数被调用,这样我们就确保函数只被调用最后一次。
代码
javascript
// fnc 代表着你要执行的函数,delay代表着你希望多长时间后用户不进行操作了然后执行函数
function Debounce(fn, delay) {
// 记录定时器的
let timer = null;
return function () {
// 如果存在timer就表示有函数正在等待执行,
//取消前面函数的等待,重新进行当前函数的等待
if (timer) { clearInterval(timer) }
timer = setTimeout(() => {
fn(键盘敲击的值); },
delay);
}
}
防抖案例代码(查看效果需要查看console.log()打印的结果):
节流
节流(Throttling)也是一种控制函数执行频率的策略,用于限制某个函数的执行频率。它的核心目的是在特定的时间间隔内,无论函数被调用多少次,都确保该函数只也一定会执行一次。
这个是重头戏
看完了防抖的原理相信你对防抖有了一定的认知,其实节流也是通过定时器来实现。节流的重点是限制某个函数的执行频率,一段时间内只执行一次!实现方法就是使用定时器,来延缓执行,如果后面函数任然进行了调用,我们就不执行函数的调用,在定时器内的函数没有调用之前其他的函数调用我全部都取消。
如果定时器内的函数已经执行完成,那么下一次函数的调用又会重新进入定时器。
下面是我的代码
javascript
// fnc 代表着你要执行的函数,delay代表着你希望执行函数的时间频率
function Throttle(fn, delay) {
let timer = null;
return function (url, keywords) {
// 如果定时器以及存在,其他函数的调用不执行直接返回
if (timer) return;
// 不存在就进入定时器
timer = setTimeout(() => {
fn(键盘敲击的值);
timer = null;
}, delay);
}
}
ok,可能这样看下来你觉得没什么问题。实际确是有不足之处,回到之前的应用场景,我们监听的是键盘的敲击事件,相信大家还记得。
你敲击键盘,某度给你的提示词是你敲击完成之后对你进行提示。但是你再仔细看看我上面的这段代码。
kotlin
if (timer) return;
这里会出现什么我们不想要的结果呢?
举个栗子,就是当我只敲击2个字时,第一个字敲击的时候定义了一个定时器假设时间为1s,那么我在接下来的任何敲击触发的函数都无法接受。而此时定时器内的函数所接收的用户输入值确是我第一次敲击所产生的,那么像后端发送的请求也是第一次敲击产生的提示词,而并非最后一次。这样的情况并不是我们所期望看见的。
以下是大佬的做法,就可以避免最后一次敲击被遗漏。
scss
// fnc 代表着你要执行的函数,delay代表着你希望执行函数的时间频率
const throttle = (func, delay) => {
// last 上一次是啥时候执行的,timer依然是记录定时器
let last, timer;
return () => {
// 当前时间,隐式转换为数字类型
// 每次用户敲击我都记录当前时间为now
let now = +new Date();
// 如果last也就是上次函数执行的时间
// 当前时间并不满足时间间隔那么我们就进入if语句
if (last && now < delay + last) {
// 如果最后一次敲击,取消定时器
clearTimeout(timer);
// 定义一个延迟函数的执行,用来满足用户的最后一次敲击被执行
timer = setTimeout(() => {
last = now;
func([键盘敲击值]);
}, delay);
}
//如果用户没有点击过或者,已经隔了delay时间没有点击就执行函数
else {
// 记录当前函数执行的时间
last = now;
func();
}
}
}
节流详细代码(查看效果需要查看console.log()打印的结果):
结语
实话,我自己在学的时候一度也认为有点抽象。但是在明白了应用场景也就是提示词需要进行最后一次点击进行提示这个功能需求后。我明白了,写本篇文章也希望给和我一样在学习防抖和节流遇到困难的小伙伴一个思路与提示。 今天的文章就分享到这,喜欢的话就点个关注或者是赞吧。谢谢你啦- ̗̀(๑ᵔ⌔ᵔ๑)