节流和防抖详解

344 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

函数防抖(debounce)

 基本概念:在事件被触发n秒在执行回调,如果在这n秒内又被触发,则重新开始计时。类型分为延时函数以及立即执行函数。

例子:日常生活中的电梯,在打开的时候会等待几秒钟然后在关闭,假设电梯等待的时间是5s ,假设场景:你在等电梯,电梯门开了你进入了,

等待五秒后电梯门自动关闭,但是在等待2s的时候又有人进来了,此时电梯感应到便重新开始计时。这就是防抖。电梯的防抖可以防止人被夹,

保证人员的安全,对于服务器来说可以保证节约服务器资源。减轻服务器压力。

延时函数示意图:

image.png

立即执行示意图:

image.png

普通防抖函数 

      function debounce(fun, delay) {
        return function () {
          clearTimeout(fun.id);
          fun.id = setTimeout(() => {
            fun.apply(this, arguments);
          }, delay);
        };
      }

优化后的防抖函数 

 function uDebounce(fn, wait) {
        let timer,
          startTimeStamp = 0;
        let args;
        // run函数 wait时间到了进入。
        let run = (timerInterval) => {
          timer = setTimeout(() => {
            // 设定当前时间
            let now = new Date().getTime();
            let interval = now - startTimeStamp;
            // 最后一次点击时间和当前时间不足awit
            if (interval < timerInterval) {
              console.log("debounce reset", timerInterval - interval);
              startTimeStamp = now;
              // 再次生成一个定时器
              run(wait - interval);
            } else {
              fn.apply(this, args);
              clearTimeout(timer);
              timer = null;
            }
          }, timerInterval);
        };

        return function () {
          args = arguments;
          // 每次触发事件便会设定startTimeStamp的时间
          startTimeStamp = new Date().getTime();
          // 初始进入或执行完回调函数可再次进入
          if (!timer) {
            run(wait);
          }
        };
      }

立即执行防抖函数 

      function rightDebounce(fn, wait, immediate = true) {
        let timer,
          startTimeStamp = 0;
        let args;

        let run = (timerInterval) => {
          timer = setTimeout(() => {
            let now = new Date().getTime();
            let interval = now - startTimeStamp;
            if (interval < timerInterval) {
              startTimeStamp = now;
              run(wait - interval);
            } else {
              if (!immediate) {
                fn.apply(this, args);
              }
              clearTimeout(timer);
              timer = null;
            }
          }, timerInterval);
        };

        return function () {
          args = arguments;
          startTimeStamp = new Date().getTime();

          if (!timer) {
            if (immediate) {
              fn.apply(this, args);
            }
            run(wait);
          }
        };
      }

普通延时防抖:

1、 在防抖函数中使用了闭包。使得fun会一直存在。从而可以清除之前生成的定时器。 2、这是一个延时的防抖函数。并且会不断的创建销毁定时器。比较消耗资源,并不是最优的防抖函数。

优化后的防抖

// 从优化后的函数可以看出 1、初始会进入run()函数,在wait期间会不断刷新startTimeStamp的值,延时函数到时间执行时判断,最后一次的startTimeStamp和now是否小于wait,是的话则再次进行,由此执行的防抖不会频繁的创建销毁定时器。除去了销毁定时器的操作。

函数防抖总结:

1、防抖函数分为延时防抖和立即执行防抖,根据具体需求情况选取。

2、防抖函数可以在一定程度上防止资源浪费,减少不必要的接口请求,从而减轻服务器的压力。

3、防抖函数的实际应用场景:窗口大小改变事件onresize、联想搜索(当input框值改变时触发接口请求)、防止按钮重复点击按钮等。

4、如果有高频触发但是中间有停顿应该选取防抖。

函数节流(throttle)

基本概念: 规定在一个单位时间内,只能触发一次函数,如果这个时间内触发多次函数,那么只有一次函数生效

例子:大家玩的MOBA类游戏都有技能,技能释放完之后都有一定的缓冲时间,在释放完毕后不停的在按也不会触发,这里使用的就是节流,如果不做这个处理从技术的角度来看,服务器肯定会爆炸。

延时节流函数 


      function throttle(fn, wait) {
        let timer, args;
        let run = () => {
          timer = setTimeout(() => {
            fn.apply(this, args);
            clearTimeout(timer);
            timer = null;
          }, wait);
        };
        return function () {
          args = arguments;
          if (!timer) {
            run();
          } else {
          }
        };
      }

立即执行节流 

      function uthrottling(fn, wait, immediate) {
        let timer,
          timeStamp = 0;
        let args;

        let run = () => {
          timer = setTimeout(() => {
            if (!immediate) {
              fn.apply(this, args);
            }
            clearTimeout(timer);
            timer = null;
          }, wait);
        };

        return function () {
          args = arguments;
          if (!timer) {
            if (immediate) {
              fn.apply(this, args);
            }
            run();
          } else {
          }
        };

节流函数总结:

1、在规定时间内只触发一次回调函数。

2、实际应用场景:下拉加载更多、onscroll

3、如果有高频触发但是没有停顿应该使用节流。

总体总结:无论是函数的节流或者防抖都是为了改善我们的代码性能,性能上去了,用户体验就会好。用户体验好了的话才能使得我们的产品持续发展下去,比如我们会在vue项目上做一些优化,分包、cdn加载资源文件、缓存、减少dom操作、压缩图片等等,但是如果你在联想搜索等场景下不做防抖节流处理,会不断的请求后台,性能差的手机肯定会卡,之前做了那么多优化,如果不处理这些又有啥意义呢?