创新编码

不说话,装高手。

Maintain silence and pretend to be an experta

什么是节流和防抖,他们有什么区别,怎么实现?

2024-07-23 17:07:55TypescriptJavascript

防抖和节流是什么

防抖和节流本质上都是 Javascript 中常用的两种性能优化方式。他们的作用是减少函数的执行次数,以提高代码性能

  • 防抖(Debounce):连续触发函数,在规定时间内只执行 最后一次。这意味着在指定时间间隔内,多次触发相同的事件,那么前面触发的事件将会被忽略,只执行最后一次触发的事件。
  • 节流(Throttle):连续触发函数,在规定时间内只执行 第一次。这意味着在指定时间间隔内,无论事件触发了多少次,都只执行一次。

84.png

如上图所示,如果不使用防抖节流函数正常执行,则会一直触发函数。这对性能有很大的影响,当使用 防抖(Debounce) 来执行函数,在指定时间间隔内,方法会将前面触发的函数过滤,只执行最后一次触发的函数,使用 节流(Throttle) 来执行函数,在指定时间间隔内无论触发多少次函数,都只执行第一次触发的函数。

应用场景

防抖(Debounce)

  • 搜索框实时搜索:在搜索框搜索关键词时,使用防抖函数延迟请求发送,只有在用户输入完成或者停顿一段时间后才触发搜索请求,避免频繁的发送网络请求。
  • 窗口大小调整:在监听 window resize 时,使用防抖来优化某些逻辑的执行频率,例如重新渲染,重新计算页面布局等,避免因为执行频率过快导致页面卡顿。

节流(Throttle)

  • 按钮点击:用户点击按钮,使用节流函数来确保只有用户点击一段时间后才执行相应操作,避免频繁操作或重复执行。
  • 页面滚动:当用户滚动页面时,使用节流函数来限制触发滚动事件的频率。
  • 鼠标移动:当用户移动鼠标时,可以使用节流来控制触发鼠标移动事件的频率。例如,在一定时间内只执行一次鼠标移动的处理逻辑,避免过多的计算和渲染操作。

总结

防抖和节流在不同的应用场景中展现出其重要性。防抖适用于需要等待用户操作完成或者停顿后才进行相应处理的情况,如搜索框实时搜索和窗口大小调整。节流则适用于需要限制事件触发频率的情况,如页面滚动加载和按钮点击。根据具体需求,选择合适的技术可以优化用户体验、减少不必要的资源消耗,从而提升前端应用的性能。

代码实现

折叠代码 复制代码
/**
 * 防抖函数
 * @param {Function} func 要执行的函数
 * @param {Number} wait 延迟执行毫秒数
 * @param {Boolean} immediate 是否立即执行
 */
const debounce = <T extends (...args: any[]) => void>(func: T, wait: number, immediate = false) => {
	let timer: NodeJS.Timeout;

    // later 函数用于设置延时执行 func
	const later = function (this: any, ...args: any[]) {
		timer = setTimeout(() => {
            // 如果不是立即执行模式,则调用原始函数 func
			if (!immediate) {
				func.apply(this, args);
			}
		}, wait);
	};

    // 返回一个函数,用于包装原始函数 func
	return function (this: any, ...args: any[]) {
		const context = this;

        // 是否立即执行的判断条件
		const callNow = immediate && !timer;
		clearTimeout(timer);

        // 如果满足立即执行条件,则立即执行 func
		if (callNow) {
			func.apply(context, args);
		}

        // 延时执行 func
		later.apply(context, args);
	} as T;
};

// 300ms内只执行最后一次触发的函数
const debouncedFunction = debounce((value: string) => {
    console.log(value);
}, 300);

debouncedFunction('1');
debouncedFunction('2');
debouncedFunction('3');
setTimeout(function() {
    debouncedFunction('4');
    debouncedFunction('5');
}, 400)

// 结果
console.log(3);
console.log(5);
折叠代码 复制代码
/**
 * 节流函数
 * @param {Function} func 要执行的函数
 * @param {Number} wait 延迟执行毫秒数
 * @param {Boolean} immediate 是否立即执行
 */
const throttle = <T extends (...args: any[]) => void>(func: T, wait: number, immediate: boolean = false): T => {
    // 定义一个标志变量,表示是否处于节流状态
    let inThrottle: boolean = false;

    // 返回一个函数,该函数将被用作节流后的函数
    return function (this: any, ...args: any[]) {
        const context = this;

        if (!inThrottle) {
            // 如果需要立即执行,则执行原始函数并进入节流状态
            if (immediate) {
                func.apply(context, args);
                inThrottle = true;
                setTimeout(() => {
                    inThrottle = false;
                }, wait);
            } else {
                // 如果不需要立即执行,同样执行原始函数并进入节流状态
                func.apply(context, args);
                inThrottle = true;

                // 在指定的等待时间后,解除节流状态
                setTimeout(() => {
                    inThrottle = false;
                }, wait);
            }
        }
    } as T;
}

const throttledFunction = throttle((value: string) => {
    console.log(value);
}, 300, true);

throttledFunction('1');
throttledFunction('2');
throttledFunction('3');
setTimeout(function() {
    throttledFunction('4');
    throttledFunction('5');
}, 400)

// 结果
console.log(1);
console.log(4);