防抖节流

Posted by CodingWithAlice on July 10, 2021

防抖节流

防抖、节流 这个写的很好,平时键盘抬起就会搜索,防抖后,间隔一段时间不输入才会搜索节流后,可以做到不管是否停止打字,间隔一段时间都会搜索

还参考了另一篇博文:JavaScript专题之跟着underscore学防抖

  • 总结 - 记住例子:

    函数防抖 - 类似法师技能读条,还没读完条再按技能,会重新读条

    • 防抖的原理就是:你尽管触发事件,但是我一定 在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就 以新的事件的时间为准,n 秒后才执行,总之,就是要等你 触发完事件 n 秒内不再触发事件,我才执行,真是任性呐!

    函数节流 - 就是 一直按着技能键,也能在规定时间内发技能

    • 如果你持续触发事件,每隔一段时间,只执行一次事件
  • 防抖 debounce - 实际应用场景
    • search 搜索联想,用户在不断输入值时,用防抖来节约请求资源
    • 浏览器 resize 的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
  • 节流 throttle - 实际应用场景
    • 鼠标不断点击触发,mousedown (单位时间内只触发一次)
    • 监听滚动事件,比如是否滑到底部自动加载更多,用 throttle 来判断

函数防抖(debounce) - 节约开销

在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

案例:一段键盘输入就执行函数的代码 - 如图,存在抖动问题

// 只要按下键盘,就会触发这次ajax请求
let ajax = (content) => {
    console.log('ajax - ', content);
}
let input = document.getElementById('input');
input.addEventListener('keyup', (e) => {
    ajax(e.target.value);
})

image-20210710163059697

将函数进行防抖处理 - 优化后:

// 通过防抖函数处理后,只有当你在指定间隔内(1s)没有输入时,才会执行函数
let ajax = (content) => {
    console.log('ajax - ', content);
}
// 添加防抖处理
let debounce = (callback, delay) => {
    return (arguments) => {
        // 清理定时器这一步十分关键 - 保证以 1s 为周期重新计数
        clearTimeout(callback.timeId);
        callback.timeId = setTimeout(() => {
            callback.call(this, arguments);
        }, delay)
    }
}
let debounceAjax = debounce(ajax, 1000);
// 给指定 input 添加监听函数
let input = document.getElementById('input');
input.addEventListener('keyup', (e) => {
    debounceAjax(e.target.value);
})

image-20210710163129879

补充:这里有一个很有趣的例子 - setInterval 里面包裹 setTimeout,讨论下时间

  • 如果外部的 delay 时间大于 防抖的 delay 时间 - 函数最终以外部时间周期执行

      let Junzhe = function () {
          console.log('🌹🌹🌹🌹');
      }
        
      // 第一次 3秒,之后每次都是 2秒 执行一次
      setInterval(debounce(Junzhe, 1000), 2000);
        
      // 第一次 4秒,之后每次都是 3秒 执行一次
      setInterval(debounce(Junzhe, 1000), 3000);
    

    原因分析如图:image-20210710195651390image-20210710195948437

  • 如果外部的 delay 时间小于 防抖的 delay 时间 - Junzhe 函数不会执行

      setInterval(debounce(Junzhe, 4000), 3000);
    

    原因分析如图:image-20210710200524848

规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。

在不断输入时,ajax会按照我们设定的时间,每1s执行一次:

let throttle = (callback, delay) => {
    let last;
    return (arguments) => {
        let now = +new Date();
        if(last && now < last + delay) {
            clearTimeout(callback.timeId);
            callback.timeId = setTimeout(() => {
                last = now;
                callback.call(this, arguments)
            }, delay)
        } else {
            // 如果现在 = 上一次的时间 + 延迟时间,那么就马上执行函数
            last = now;
            callback.call(this, arguments)
        }

    }
}
let throttleAjax = throttle(ajax, 1000);

let input3 = document.getElementById('input3')
input3.addEventListener('keyup', (e) => {
    throttleAjax(e.target.value);
});

image-20210710203800964

解决上文外部的 delay 时间小于 防抖的 delay 时间 - Junzhe 函数不会执行

// 不管我们设定的执行时间间隔多小,总是1s内只执行一次。
setInterval(throttle(jun,3000),2000);

看一下打印结果:

image-20210711133823106

分析 - 利用定时执行函数里面 now闭包,将 last 时间设置成定时任务开始的时间:

image-20210711133948409