防抖动与节流阀(debouncing && throttling)

一个使用场景:
某些浏览器事件可能会在短时间内高频触发,比如:整窗口大小或滚动页面。
如果给窗口滚动事件添加一个事件监听器,然后用户不停地快速滚动页面,那你的事件可能在短短数秒之内被触发数千次。这会导致非常严重的性能问题。
所以如果功能涉及滚动事件,窗口调整事件,或者键盘事件鼠标事件等,你可能需要警觉起来,是否有必要使用 debouncing 或者 throttling 来提高页面速度与性能。

Debouncing(防抖动)

概念

debouncing(防抖动)是解决上述问题的一个方案,它的做法是 限制下次函数调用之前必须等待的时间间隔,也就是说:*强制一个函数在某个连续时间段内只执行一次,哪怕它本来会被调用多次*。正确实现 debouncing 的方法是:将若干个函数调用合并为一次,只有在空闲时间大于或等于给定值的时候,才执行调用方法。

实现

简单的实现一个 debounce 方法,接收两个参数,一个是需要防抖动的函数 fn,另一个是延迟时间delay

funciton debouncing(fn, delay) {
    let timer; //定时器
    return function() { 
        // 保存函数调用时的上下文和参数,传递给 fn
        const context = this;
        const args = arguments;
        
        clearTimeout(timer);
        
        timer = setTimeout(function(){
            fn.apply(context, args);
        }, delay);
    }
}

debounce 的使用方法如下:

$(document).on('keyup', debounce(function(e) {
    // 代码
}, 250))

Throttling(节流阀)

概念

throttling(节流阀)则是另一种解决问题的方案,它的做法是*固定一段时间内函数调用的频率*,它与 debouncing 最大的不同之处在于,throttling 会保证至少调用一次。

实现

debounce 类似,接收两个参数,一个是需要截流的函数 fn, 另一个是函数执行间隔阈值 threshhold

function throttle(fn, threshhold) {
    let timer; //定时器
    let last; //记录上次时间
    threshhold || (threshhold = 250); //默认间隔为250ms    
    return function() {
        // 保存函数调用时的上下文和参数,传递给 fn
        var context = this;
        var args = arguments;
        
        let now = +new Date();
        
        // 如果上次调用距本次调用的时间间隔不够,则不执行 fn,并重新计时
        if(last && now < last + threshhold){
            clearTimeout(timer);
            
            // 保证在当前时间区间结束后,再执行一次 fn
            timer = setTimeout(()=>{
                last = now;
                fn.apply(context, args);
            }, threshhold);                
        } else {  //如果时间间隔够了,则立刻执行 fn
            last = now;
            fn.apply(context, args);
        }   
    }
}

throttle使用方法如下:

$(document).on('mouvemove', throttle(function(e) {
    // 代码
}, 250))

总结

debouncing 和 throttling 的区别还是很明显的:前者把一段时间的多次调用合并成一次,后者把一段时间的多次调用减少为数次。下图的展示十分便于理解。
debouncing 和 throttling 效果对比

Comments
Write a Comment