手把手教你实现一个防抖函数(debounce)(一)

简介: 手把手教你实现一个防抖函数(debounce)

image.png

手把手教你实现一个防抖函数


前言:防抖函数在日常开发中属于是一个非常非常重要的知识点。通常在一个项目的最开始构建的时候,都会在 utils文件夹下备上这样一个函数,来为以后做准备。 (tipsutils 在大部分翻译软件内好像都叫跑龙套的,这个翻译不是那么合理。这个单词在这个场景下更像存放工具类的函数的文件夹。通常我们会放一些比如格式化时间,格式化文件大小格式,节流之类的函数。)

这篇文章原意是想紧随在姊妹篇文章节流函数的原理之后发布的。但是那时候自己对闭包、高阶函数的概念不是特别清楚,害怕误导读者,故拖了比较久的才发布这个重要知识点。

注:本文不会讲解防抖的高级写法,只会一步一步带你理清思路,如何拓展功能还需各位看官举一反三。

一. 什么是防抖?使用场景是什么?


首先我们要知道,这里的防抖具体指的是什么?我们假设一个场景,这里就拿我们日常最常用的功能,《搜索〉来举例子。

image.png

我们用 v-model 指令绑定这个 <input> 框。然后绑定一个根据用户输入的关键词,去后端数据库检索数据的模拟函数。(这里我们用 console 代替)。

image.png

image.png

然后我们用 watch 去监视 searchKeyword 的变化,每当用户输入关键词后,我们就向后端发起一次请求。

image.png

我们可以非常明显的看到,在这种情况下。我仅仅只是想最后搜索 hanzhenfang 这几个关键词,但是我在输入每一个字符的时候,都会去后端请求一次,数据量小还好,如果数据量过大的话,由于前几次的请求都是毫无意义的,势必会造成性能和资源上的浪费。

image.png

4.什么?你说为什么不等最后点击搜索按钮的时候再去搜索? emm... 这个确实是可以。但是突然有一天,产品经理说:“这个搜索框如果有联想功能的话就更好了!我们要赶超百度,赛过谷歌!”你怎么办嘞?目前的情况到不是不行🤔,就是有可能挨后端的一顿毒打(bushi)“...服务器为什么老莫名挂”

f8e1d41e35f54ef9a97c193d6b4d0b8b_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.jpg

ok,现在压力来到了前端这边。接口该调还是得调,但是我希望他在我输入完 hanzhenfang 的时候,然后检测我没有继续往下输入了,再去调后端的接口,然后我再把返回的联想词联系给它展示在这里是不是就可以了呢?

二.理清思路


让我们转化一下思路,只是单纯的这样说你可能不太理解。我们换一个更为简单的场景。

image.png

image.png

现在页面只有一个简单的按钮,通过点击这个按钮,我们会向后端发起请求。(这种场景我知道有很多别的限制方法🚫,比如在某个时间段内把按钮的 disabled 属性改为 true 等等,我们暂时不讨论这种解决方案。)

image.png

现在我们尝试疯狂点击按钮就会疯狂发送请求。

7fb98c5edda2449a97716478d395e94a_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.jpg

我们现在来修改一下这个函数,我们思考一下🤔,假设我们不借助 debounce 可以实现一个伪防抖的功能吗?答案是百分百可以的。我们先在这个文件下设定一个数字类型的变量叫做 timerID。稍后我会告诉你为什么是数字类型的。(tips:其实也不是特别需要限制类型 null 这些的也可以)

image.png

然后我们设定一个定时器,来使这个 console.log("发请求")1.5s 后执行。

image.png

我担心个别读者对《 setTimeout 是有返回值的》这件事不是特别了解。我来穿插讲解一下你可能不知道的知识

image.png

image.png

其实 setTimeout 会在 setTimeout 执行的时候返回一个大于 0 的正整数。 所以我们这句话其实是在给 timerID 赋值! 并不是将 setTimeout 函数本身赋值给 timerID 这个变量。

image.png

⚠️注意: 全文重点是下面这句话:

image.png

这里我们需要特别搞清楚 setTimout函数本身执行的时候,是马上赋值的,并不是等到 1.5s 后再赋值的。我希望你多读几句这句话,一定要理解这个概念!

什么意思呢? 我设置了大约在10几年后再执行的一个函数,千万不要觉得 timerID 是会在10年以后才会被赋值。 什么?你不信?来给你演示一下。

image.png

64b5a5eac91144bbbe703b0b802b0dae_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.jpg

为什么要这样设计呢?因为如果这样执行的话,就会给我们一个反悔的机会。还说上面的例子。假设我在 5 年后突然反悔不想执行了。我只需要取消这个 timerID 就可以中途放弃执行。

我们编写一个 cancleSearch 函数,这个函数非常简单。就是一个调用了 clearTimout 这个取消定时器的方法,并且我们把定时器的延时设定为3s。

image.png

演示一下:

735c1c4b658b4962bbc3c3e4dc863129_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.jpg

我的前两次请求已经被我成功阻止了。第三次由于我没点击取消,从而正确的在 3s后帮我执行了 getSearch 函数。

聪明的你可能已经想到了,这个 timerID 就是每一个 setTimeout 的身份证。每当你执行一次setTimout 后,setTimout 所接收的回调函数就会被分配一个唯一 ID,来被放进任务队列。注意!!!一旦任务顺利从任务队列被推进主线程执行后,这个唯一 id 其实作用也就没什么特别大的意义了。

image.png

clearTimeout 的功能恰好就是清除位于任务队列里指定的 id 所绑定的那个回调函数。

三. 实现一个简单的自我防抖函数


由上面的前备知识,我们就可以实现一个非常简单的自我防抖函数。接下来我梳理一下思路。

image.png

当我们每次执行 getSearch 之前,如果当前任务队列里有上一次同样的任务,我们就先清除掉。

image.png

然后再去开启一个定时器任务推进任务队列。

image.png

至此我们就做到了该函数本身一个简陋的防抖。测试一下,在此之前我们设定一个计算我们点击了多少次按钮的变量,该函数仅仅是为了计数而已。

image.png

我们测试一下:

860844b5c25d407db039a0f5d70ce558_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.jpg



相关文章
|
5月前
15 # 手写 throttle 节流方法
15 # 手写 throttle 节流方法
19 0
|
4天前
|
前端开发 JavaScript UED
【专栏】如何理解 debounce(防抖)和 throttle(节流)
【4月更文挑战第29天】前端开发中,为解决用户操作引发的性能问题,常使用debounce(防抖)和throttle(节流)技术。Debounce确保在一段时间内只执行最后一次事件触发的操作,减少不必要的执行,但有滞后性,适合搜索框实时搜索。Throttle则保证一定时间间隔内函数执行一次,保持固定频率,适用于滚动事件处理和窗口大小调整。两者可借助JavaScript或第三方库实现,需根据场景和需求选择并调整。正确使用能提升应用性能和用户体验。
|
2月前
|
UED
函数防抖
在频繁触发的情况下,只有足够的空闲时间,才执行代码一次,如果没有执行完就清除掉,重新执行逻辑。简单来说,当触发后再次触发,会取消上一次触发的执行,直到最后一次触发后过去设定时间后才执行。
27 5
|
3月前
|
算法 前端开发
2627. 函数防抖
2627. 函数防抖
15 0
|
5月前
14 # 手写 debounce 防抖方法
14 # 手写 debounce 防抖方法
24 1
|
25天前
|
前端开发
【前端学习】—函数防抖(十)
【前端学习】—函数防抖(十)
|
8月前
|
前端开发
手写防抖节流
手写防抖节流
手写防抖节流
面试官:防抖和节流的区别是啥?实现一个防抖和节流函数(二)
面试官:防抖和节流的区别是啥?实现一个防抖和节流函数(二)
|
9月前
|
前端开发 UED
面试官:防抖和节流的区别是啥?实现一个防抖和节流函数(一)
面试官:防抖和节流的区别是啥?实现一个防抖和节流函数(一)
|
9月前
|
JavaScript
原生js实现一个节流函数和防抖函数?
原生js实现一个节流函数和防抖函数?
51 0