节流与防抖---性能优化

85 阅读1分钟

背景

优化高频率事件,降低代码执行频率。有些事件是会频繁触发的:onkeyup、keydown、onscroll、onresize、oninput、mouseMove、等 我不想让它如此的频繁,怎么办呢?

  • 我不想让它如此的频繁,只在最后一次事件后触发即可,debounce
  • 我依然想让它频繁触发,只不过不要那么频繁, throttle
注:UnderScore 中是 throttle & debounce 两个方法,lodash把2个方法合一起了,封装逻辑稍有复杂

防抖Debounce

一段时间结束后,才能触发一次(so,不适合抢购按钮,用户要一直可以点击),如果一段时间未结束再次触发事件,就会重新开始计算时间

  • 应用场景:百度联想查询:不停止输入就不call api,直到停止输入后才call api
  • 怎么实现呢? 在事件发生时,取消上一次的定时器,开启新的定时器

/**
 * immediate:首次,是否立即执行一次
*/
function debounce(fnc, wait) {
    let timeout;
    return function () {
        clearTimeout(timeout)
        if (immediate && !timeout) {
            fnc.apply(this, arguments)
        }
        timeout = setTimeout(() => {
            fnc.apply(this, arguments)
        }, wait);
    }
}

节流throttle

  • 场景:1s内的点击了3次,算一次点击,窗口的resize事件

image.png


/**
 * wait:时间段
 * options.trailing=true 最后一次要不要执行
 * options.leading=false 第一次要不要执行
*/
function throttle(fn, wait, options) {
    let previous = 0; //初始值是0,第一次一定大于wait
    let context = this
    let args;
    let timeout;
    let laterFn = function () {
        fn.apply(context, args)
        previous = options.leading === false ? 0 : Date.now()
        timeout = null
    }
    let throttled = function () {
        args = arguments
        let now = Date.now()
        if (!previous && options.leading === false) {
            previous = now
        }
        let remaining = wait - (now - previous)
        if (remaining < 0) {
            if (timeout) {
                clearTimeout(timeout)
                timeout = null
            }
            fn.apply(context, arguments)
            previous = Date.now()
        } else if (!timeout && options.trailing) {
            timeout = setTimeout(laterFn, remaining);
        }
    }
    return throttled;
}

节流防抖的区别

  • 相同点:都是为了降低频率
  • 不同点:

image.png