我理解的函数的节流&防抖

1,367 阅读3分钟

节流&防抖都是用来高频触发优化方式

  • 函数防抖: 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时(若干函数合并成为一次)
  • 函数节流: 规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效(当达到了一定时间间隔就会执行一次)

函数节流(throttle)与 函数防抖(debounce)都是为了限制函数的执行频次,以优化函数触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象

防抖

当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。如下图,持续触发scroll事件时,并不执行handle函数,当1000毫秒内没有触发scroll事件时,才会延时触发scroll事件。 防抖实现

function debounce(fn, wait) {
    var timeout = null;
    return function() {
        if(timeout !== null) 
                clearTimeout(timeout);
        timeout = setTimeout(fn, wait);
    }
}
// 处理函数
function handle() {
    console.log(Math.random()); 
}
// 滚动事件
window.addEventListener('scroll', debounce(handle, 1000));

当持续触发scroll事件时,事件处理函数handle只在停止滚动1000毫秒之后才会调用一次,也就是说在持续触发scroll事件的过程中,事件处理函数handle一直没有执行。

原理
通过定时器将回调函数进行延时.如果在规定时间内继续回调,发现存在之前的定时器,则将该定时器清除,并重新设置定时器

也可以使用lodash进行调用使用

// Avoid costly calculations while the window size is in flux.
jQuery(window).on('resize', _.debounce(calculateLayout, 150));
 
// Invoke `sendMail` when clicked, debouncing subsequent calls.
jQuery(element).on('click', _.debounce(sendMail, 300, {
  'leading': true,
  'trailing': false
}));
 
// Ensure `batchLog` is invoked once after 1 second of debounced calls.
var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
var source = new EventSource('/stream');
jQuery(source).on('message', debounced);
 
// Cancel the trailing debounced invocation.
jQuery(window).on('popstate', debounced.cancel);

节流

持续触发scroll事件时,并不立即执行handle函数,每隔1000毫秒才会执行一次handle函数。 节流实现

function _throttle(fn,wait,time){
    var previous = null; //记录上一次运行的时间
    var timer = null;

    return function(){
        var now = +new Date();

        if(!previous) previous = now;
        //当上一次执行的时间与当前的时间差大于设置的执行间隔时长的话,就主动执行一次
        if(now - previous > time){
            clearTimeout(timer);
            fn();
            previous = now;// 执行函数后,马上记录当前时间
        }else{
            clearTimeout(timer);
            timer = setTimeout(function(){
                fn();
            },wait);
        }
    }
}
function _log(){
    console.log(1)
}
window.onscroll = _throttle(_log,500,2000)

原理
防抖是将多次执行变为最后一次执行,节流是将多次执行变为在规定时间内只执行一次.一般不会重置定时器

我们在处理移动端scroll事件的时候会进行节流处理

handleScroll() {
    // 滚动顶部高度+可视区域高度 > 滚动高度 - 触发临界点
    /* eslint-disable max-len */
    const LOAD_OFFSET = 200;
    const needLoad = ((document.documentElement.scrollTop || document.body.scrollTop) + document.documentElement.clientHeight) > (document.body.scrollHeight - LOAD_OFFSET);
    if (needLoad) {
        this.loadmore();
    }
}

会给个判断,如果滚动顶部高度+可使区域>滚动高度 - 出发临界点才会进行数据请求,当然也可以使用setTimeout进行处理

大致dome如下:
var canRun = true;
document.getElementById("throttle").onscroll = function(){
    if(!canRun){
        // 判断是否已空闲,如果在执行中,则直接return
        return;
    }

    canRun = false;
    setTimeout(function(){
        console.log("函数节流");
        canRun = true;
    }, 300);
};

当然,使用lodash是最佳实践


jQuery(window).on('scroll', _.throttle(updatePosition, 100));
 
// Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
jQuery(element).on('click', throttled);
 
// Cancel the trailing throttled invocation.
jQuery(window).on('popstate', throttled.cancel);

结合应用场景

  • debounce
    • input, keyup
  • throttle
    • 鼠标不断点击触发,mousedown(单位时间内只触发一次)
    • 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断
    • touchmove
    • resize