前言:
首先让我解释一下什么是节流和防抖
一、防抖(debounce):触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间
举例:就好像在百度搜索时,每次输入之后都有联想词弹出,这个控制联想词的方法就不可能是输入框内容一改变就触发的,他一定是当你结束输入一段时间之后才会触发。
二、节流(thorttle):高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率
举例:预定一个函数只有在大于等于执行周期时才执行,周期内调用不执行。就好像你在淘宝抢购某一件限量热卖商品时,你不断点刷新点购买,可是总有一段时间你点上是没有效果,这里就用到了节流,就是怕点的太快导致系统出现bug。
二者的区别:防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。
理解:防抖主要是用于高频次触发的方法 转换成 最后一次的执行,例如 搜索、点击字母排序的某个字母
而节流主要是用于高频次触发的方法 转换成 这些触发的次数再每隔一段时间执行一次
案例:(1)当一个搜索框需要实现实时搜索的时候,输入框输入,请求后台接口,第一个接口返回的信息可能比较慢,到第二次调用后信息已经返回了,前一条数据才出来,如何避免页面被第一个接口返回的信息覆盖?
这个时候就可以结合节流来解决高频次调用接口,并且在调用第二次接口的时候将上一次的接口调用给cancel掉,这样就可以避免上一次的接口返回的数据覆盖第二次接口返回的数据
上代码,让理解更加深刻
// 保存axios实例,使用于cancel请求
const queryCitysServiceInstance = React.useRef<ReturnType<typeof queryCitys>>(queryCitys());
// 节流实时搜索的逻辑
const queryCitysThrottler = React.useRef(
throttle(async (newKW: string) => {
setLoading(true);
const resp = await queryCitysServiceInstance.current.axios({
data: {
keys: newKW,
},
});
//数据处理resp
}, 200)
);
const onKeywordChange = React.useCallback(
async (newKW: string) => {
try {
queryCitysServiceInstance.current.cancel();
newKW && queryCitysThrottler.current(newKW);
} catch (e) {
// 被终止的请求,不做任何处理
if (e.errorMsg === "cancel") return;
}
},
[setKeyword]
);
首先定义一个axios的实例后面cancel请求的时候使用,在搜索框进行搜索的时候每次调用接口的时候都会之前的请求给cancel掉,并且限制0.2秒内只会搜索一次
其实最开始使用 useCallback 的理由中,只有「需要保存一个函数闭包结果,如配合 debounce、throttle 使用」这个是真正需要使用 useCallback 的,其他的都可能带来风险,所以除非你对useCallback和useMemo已经非常理解了才去使用它,要不然带来的效果可能就是不升反降
使用到 useCallback的场景:
1、当这个函数会被传递给子组件,为了防止组件频繁渲染,使用useCallback包裹,保持引用不变
2、需要保存一个函数闭包结果,如配合 debounce、throttle 使用
哈哈哈哈,我也不是很会使用useCallback和useMemo
https://blog.csdn.net/sinat_17775997/article/details/94453167
三、手写节流防抖函数
**节流:**某个函数在指定时间段内只执行第一次,直到指定时间段结束,周而复始。
在解释这个函数之前,先理解下闭包的概念:在 JavaScript 中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中,我们就把这些变量的集合称为闭包。
以下面这个函数为例,flag就是外部函数throttle的变量,当throttle函数执行完毕之后,flag变量并不会马上销毁掉,而是继续保存在内存中,而且保存上一次的值。
只需要设置一个标志位,利用闭包的特点就可实现。
function throttle(fn, interval = 200) {
let flag = null;
return function(...args) {
if (!flag) {
flag = true;
setTimeout(() => {
flag = false;
fn.call(this, ...args);
}, interval);
}
}
}
防抖:某个函数在短时间内只执行最后一次。
防抖就是在执行一次的函数之前将之前的延时给清除掉,然后重新开始一段新的延时。
function debounce(fn, delay = 200) {
let lastFn = null;
return function(...args) {
if (lastFn) {
clearTimeout(lastFn);
}
let lastFn = setTimeout(() => {
lastFn = null;
fn.call(this, ...args);
}, delay);
}
}