节流与防抖用于函数被多次触发时,不同的处理方式
异同
相同点
都是为了处理函数触发的频率。
不同点
防抖:在函数触发n秒后再执行回调,在时间阈值内再次调用函数,将清除掉上次执行的回调,重新计算时间。
节流:函数触发的n秒内,再次调用函数,将不会处理,只有过了时间阈值才能再次触发。
既:在小于时间阈值内多次调用函数,防抖只会执行一次,而节流可以执行多次。
简单实现
/**
* 防抖
* @param fn 回调函数
* @param delay 时间阈值
* @returns 函数
*/
export function debounce(fn: (...args: unknown[]) => void, delay: number) {
let timer: any = null
return (...args: unknown[]) => {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn(...args)
}, delay)
}
}
/**
* 节流
* @param fn 回调函数
* @param delay 时间
* @returns 函数
*/
export function throttle(fn: (...args: unknown[]) => void, delay: number) {
let valid = true
return (...args: unknown[]) => {
if (!valid) {
return
}
valid = false
setTimeout(() => {
fn(...args)
valid = true
}, delay)
}
}
上面的方法简单的实现了防抖与节流,但实际应用中,有时需要回调函数立即执行,或者取消已经执行但还未触发回调的函数,那就需要进一步封装了。
进一步实现
/**
* 防抖函数
* @return 防抖函数
* @param func 要执行的函数
* @param wait 间隔时间(毫秒)
* @param immediate 是否立即执行
*/
export function debounce(
func: (...args: unknown[]) => void,
wait: number,
immediate?: boolean,
) {
let timer;
return (...args: unknown[]) => {
if (timer) {
clearTimeout(timer);
}
if (immediate) {
const isCan = !timer;
timer = setTimeout(() => {
timer = null;
}, wait);
if (isCan) {
func(...args);
}
} else {
timer = setTimeout(() => {
func(...args);
}, wait);
}
return () => {
if (timer) {
clearTimeout(timer);
}
};
};
}
/**
* 节流函数
* @return 节流函数
* @param func 要执行的函数
* @param wait 间隔时间(毫秒)
* @param immediate 是否立即执行
*/
export function throttle(
func: (...args: unknown[]) => void,
wait: number,
immediate?: boolean,
) {
let previous;
return (...args: unknown[]) => {
if (immediate) {
const now = Date.now();
if (now - previous > wait) {
func(...args);
previous = now;
}
} else {
if (!previous) {
previous = setTimeout(() => {
previous = null;
func(...args);
}, wait);
}
}
return () => {
if (previous) {
clearTimeout(previous);
}
};
};
}
使用上面的方法,创建的防抖和节流函数会返回取消方法,但仅能取消还未执行的回调函数,对于立即执行的无效。
下面是测试的例子:
其他的就不测试了。
用途
1、富文本编辑实时保存问题,可以使用节流函数。
2、窗口大小改变或者鼠标滚动,根据需求使用。
3、浏览器无法监听页面停止滚动,可以再页面滚动时添加防抖函数,来处理停止滚动的逻辑。比如本页面,在页面滚动时会让小猫动画运行,页面停止滚动则暂停动画,暂停动画就是使用防抖函数在页面滚动时添加的。
4、其他,还是看需求。
后语
上面封装的函数都是使用ts写的,用的es6的箭头函数,若需要使用es5语法,为防止this指向改变,可以使用arguments和apply来实现this指向和参数传递。
...
return function () {
const content = this;
const args = arguments;
...
fun.apply(content, args)
...
}
...