# 前言
对于防抖和节流,相信大家都已经听了太多太多,网上在这一块的资料也有很多。在真正使用它之前我也曾按照网上的教程手写过几个不同的版本,最近在工作中真正要用上的时候,才发现之前对其的理解还不够深刻。所以今天在这里好好的梳理一下它们,贴上一个比较全的实现方式,另外介绍一下实际工作中是怎样使用的。
# why 防抖节流?
首先想要说明的是为什么需要使用到防抖和节流,若是已经清楚的小伙可以直接跳过阅读后面的内容,若你和我一样在使用场景上还存在一些疑惑的话可以仔细看看这一块内容,我会尽量说的清晰一些。
场景一
有这么一个需求,用户在点击某个按钮的时候会触发一些任务事件。但若是他在很短的时间内连续的点击按钮,则会不停的触发任务事件,但我们想要这个任务事件在一定时间内只能触发一次(不管按钮被点击了几次),然后过了这个时间再次点击就又可以触发了。
如图:
此时,像这种频繁的触发任务事件,但却只想要它在一定时间内只执行一次,过了这个时间又能继续触发的情况我们就称之为防抖。
但在实际工作中,我们发现上面避免按钮重复点击的情况一般都可以给按钮增加一个loading的效果来避免,用的更多的可能是下面的场景。
场景二
用户在输入框输入文字之后,需要将输入的文字发送给后台并获取到数据。我们知道这样的需求一般都是通过监听输入框内容的改变,然后将最新的内容传递给后台。但若是每输入一个字或者一个拼音就发送一个请求是否就会产生大量多余的请求,此时,使用防抖无疑是非常有必要的。
# 防抖
# 非立即执行防抖
相信大家看到上面的需求就知道,防抖的实现肯定离不开定时器了,所以我们先来实现一个简易版的防抖:
// func是用户传入需要防抖的函数
// wait是等待时间
const debounce = (func, wait = 500) => {
// 缓存一个定时器id
let timer = 0
// 这里返回的函数是每次用户实际调用的防抖函数
return function(...args) { // 接收额外的参数
if (timer) clearTimeout(timer) // 如果已经设定过定时器了就清空上一次的定时器
timer = setTimeout(() => { // 开始一个新的定时器,延迟执行用户传入的方法
func.apply(this, args)
}, wait)
}
}
可以看到,上面的debounce函数利用定时器将要执行的函数func放到事件队列中,等过了wait时长后就会执行。若是在还没过wait时长就再次触发了就会清空掉上一个定时器(也就是取消了上次的func),从而重新计算func执行的时间。
了解了原理之后,我们就知道上面的函数并不会在一开始就执行,而是在过了wait时长之后才执行,像这种情况,我们称它为非立即执行防抖。
案例1🌰
<body>
<input id="text" onkeyup="changeInput(event)" />
<p id="result"></p>
<script>
let text = document.querySelector('#text')
let result = document.querySelector('#result')
function changeInput (event) {
// console.log(event.target.value)
debouncePostValue(event.target.value)
}
function debounce (