相信很多人在面试的时候都会被问到节流和防函数,那么节流和防抖函数到底是什么呢,在我们的日常工作中又会有哪些场景会使用到呢
防抖函数
防抖函数,听上去总觉得脑海里出现很多让人瑟瑟发抖的表情包

那让我们从一个比较实际的场景触发,比如我们在使用百度搜索的时候,当我们在输入的时候,搜索框下会的推荐关键词列表会实时改变

但是如果当我们每一次输入都请请求一下后端的推荐接口的话,可能会对后端服务造成非常大的压力,而且页面也会因为接口返回的数据不断变化而导致页面的不断redraw,从而页面产生抖动。
在这种场景下用户也并不是需要那么实时结果,而且当用户在快速输入的时候,其实输入到一半的数据往往也不是用户想要的最终内容。
所以我们的需求就是 需要在用户快速输入的时候先不发送接口请求,而在用户停止输入的时候尽快发送(停止输入表示用户可能已经输入了想要的搜索内容)
因为我们无法非常准确知道什么时候是用户已经完成输入了,但是我们可以设定一个时间,比如500ms内用户没有再次输入了,那么我们就认为用户已经完成了输入
const throttleTime = 500;
let timer = null
function onInput() {
// 清除之前的定时器(如果在500ms内触发的第二次输入,那么之前的定时器就会被清除,如果是500ms后的,因为请求已经发送了,也可以直接清除)
clearTimeout(timer)
// 设定一个定时器500ms后发送请求
timer = setTimeout(sendRequest, throttleTime);
}
简单的几行代码就已经完成了我们需求,但是现在有一个问题,我们的代码最快也要在用户输入的第一次以后的500ms,有时候我们想要用户在第一时间先看到一些内容,在视觉体验上会好一些,那么我们需要简单的调整一下
const throttleTime = 500;
let timer = null
function onInput(...args) {
// 清除之前的定时器(如果在500ms内触发的第二次输入,那么之前的定时器就会被清除,如果是500ms后的,因为请求已经发送了,也可以直接清除)
// 保存一下当前的参数和作用域
const that = this;
if (!timer) {
sendRequest.apply(this, args)
// 设定一个无操作的定时器,其实也可以直接设定timer = 1
timer = setTimeout(() => {}, throttleTime);
} else {
clearTimeout(timer)
// 设定一个定时器500ms后发送请求
timer = setTimeout(() => {
sendRequest.apply(this, args)
}, throttleTime);
}
}
到这一步,我们发现,我们的timer
和我们业务代码onInput()
,sendRequest()
已经完成耦合在一起了,如果有另外有一个业务功能需要使用,又需要设置一遍同样的内容。
那我们需要把我们写的这个功能抽象出来,封装成一个可以被复用的功能
const throttle = function(func, throttleTime) {
let timer = null;
return function(...args) {
const that = this;
if (!timer) {
func.apply(this, args)
// 设定一个无操作的定时器,其实也可以直接设定timer = 1
timer = setTimeout(() => {}, throttleTime);
} else {
clearTimeout(timer)
// 设定一个定时器500ms后发送请求
timer = setTimeout(() => {
func.apply(this, args)
}, throttleTime);
}
}
}
// 使用
$input.onInput(throttle(sendRequest, 500));
到这里我们其实就已经完成了一个基本的节流函数了
节流函数可以用在一些高频触发的场景下,用来控制回调函数的触发次数,就像我们一直按住枪的开关,只要不松手,子弹就不会射出去
节流函数
节流,从字面上来看就是减少触发的次数,直觉上还是有些不好理解,也不太清楚具体的场景
我们还是从一个例子来看吧,当我们页面刷有很多小视频的时候,我们实时的监听滚动到了什么位置,来判断滚动到了什么位置,来使对应的位置的视频自动播放。
因为页面的滚动的回调非常的频繁,同时回调函数里面获取视频的位置(offsetTop
)也是需要一些时间,会导致大量不必要的计算,会导致其他的任务被hold
那我们可以设定一个规则,比如500ms内我们的回调函数只会被触发一次,且是最后一次,就可以使回调的次数大量减少,且不会影响功能。
const debounceTime = 500;
let lastTriggerTime = 0;
function scoller(...args) {
const now = new Date().getTime();
// 当触发的时间距离上一次的时间已经超过500ms,那么就可以触发一次
if (now - lastTriggerTime > debounceTime) {
videoStart.apply(this, args)
lastTriggerTime = now
}
}
我们同样也需要将这个功能封装成一个可以被复用的函数
function debounce(func, debounceTime) {
let lastTriggerTime = 0;
return function(...args) {
const now = new Date().getTime();
const that = this;
if (now - lastTriggerTime > debounceTime) {
func.apply(this, args)
lastTriggerTime = now
}
}
}
window.onscroll(debounce(videoStart, 500));