美文网首页
防抖和节流

防抖和节流

作者: 风向应该可以决定发型吧 | 来源:发表于2020-11-18 01:14 被阅读0次

    防抖和节流

    防抖

    在准备执行某项操作时,预设一个间隔时间,然后计时,达到预设的时间间隔之后才执行逻辑;
    在没到达间隔时间这段时间内,如果有多次重复操作,那么后面的操作会顶替掉前面的计时任务,重新开始计时。

    应用场景: 输入框输入内容过程中去服务器查询数据,不能每次input都去查询,所以只有当用户输入停顿一定时间间隔时,认为用户希望获得查询结果。

    代码如下:

    /**
     * 防抖: 重复操作重置定时器
     */
    function debounce (callback, delay, immediate) {
      let timer, context, args, isExecuted;
    
      let run = () => {
        timer = setTimeout(() => {
          !isExecuted && callback.apply(context, args);
          clearTimeout(timer);
          timer = null;
        }, delay)
      }
    
      return function() {
        context = this;
        args = arguments;
        // 重复进入时,设置为false,用于 run内部进行判断
        // 若没有此标志位,run内只能用 immediate 来判断优化
        // 但问题是,如果使用immediate,那么定时器完成后始终不会执行callback
        isExecuted = false;
    
        // 定时器存在时,清空并重建
        // 没有必要释放timer,因为run方法会重新赋值
        if (timer) {
          clearTimeout(timer);
          run()
        } else {
          // timer不存在的两种情况
          // 1. 初始状态,还没有创建任何定时器
          // 2. 完成了一次执行,timer被释放
          // 所以当timer不存在时,需要创建;
          // timer存在时,说明还在进行计时,此时需要清除定时器并重新创建
          !!immediate && callback.apply(context, args);
          isExecuted = true; // 标记为已执行
          run();
        }
      }
    }
    

    测试代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>节流Throttle</title>
      <style>
        #container {
          width: 200px;
          height: 200px;
          text-align: center;
          line-height: 200px;
          background-color: gray;
          color: white;
        }
      </style>
    </head>
    <body>
      <input id="input" type="text">
      <span>input输入结果</span>
      <script>
        window.onload = () => {
          var inputEl = document.getElementById('input');
    
          inputEl.addEventListener('input', debounce(function(e) {
            this.nextElementSibling.textContent = this.value
          }, 1000, true));
        }
    
        /**
         * 防抖: 重复操作重置定时器
         */
        function debounce (callback, delay, immediate) {
          let timer, context, args, isExecuted;
    
          let run = () => {
            timer = setTimeout(() => {
              !isExecuted && callback.apply(context, args);
              clearTimeout(timer);
              timer = null;
            }, delay)
          }
    
          return function() {
            context = this;
            args = arguments;
            // 重复进入时,设置为false,用于 run内部进行判断
            // 若没有此标志位,run内只能用 immediate 来判断优化
            // 但问题是,如果使用immediate,那么定时器完成后始终不会执行callback
            isExecuted = false;
    
            // 定时器存在时,清空并重建
            // 没有必要释放timer,因为run方法会重新赋值
            if (timer) {
              clearTimeout(timer);
              run()
            } else {
              // timer不存在的两种情况
              // 1. 初始状态,还没有创建任何定时器
              // 2. 完成了一次执行,timer被释放
              // 所以当timer不存在时,需要创建;
              // timer存在时,说明还在进行计时,此时需要清除定时器并重新创建
              !!immediate && callback.apply(context, args);
              isExecuted = true; // 标记为已执行
              run();
            }
          }
        }
    
      </script>
    </body>
    </html>
    

    节流

    当短时间内重复执行某项操作时,予以忽略,只执行一次;知道执行完成之后才会重新添加执行能力。

    实现原理: 维护一个定时器,每次执行操作是都判断定时器是否存在,如果定时器存在,直接return;
    如果定时器不存在,则创建定时器,定时器到期后执行,并清除定时器和定时器标志

    代码如下:

    const throttle = (callback, delay, immediate) => {
      let timer, context, args;
    
      let run = () => {
        timer = setTimeout(() => {
          // 仅在不是立即模式时执行,防止二次执行
          // 当 immediate 为 true时, run方法的作用仅用于创建定时器,用于下次执行控制
          if (!immediate) callback.apply(context, args)
    
          clearTimeout(timer) // 清除定时器,
          timer = null // 回收timer,防止对后面的执行造成影响
        }, delay)
      }
    
      return function() {
        // 存储上下文和实参列表
        context = this;
        args = arguments;
    
        // 当前如果有定时器任务,则取消操作
        if (timer) return
    
        // 如果需要立即执行,则执行
        if (immediate) callback.apply(context, arguments)
        // 再次执行run方法创建定时器用于下次判断
        run()
      }
    }
    

    测试:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>节流Throttle</title>
      <style>
        #container {
          width: 200px;
          height: 200px;
          text-align: center;
          line-height: 200px;
          background-color: gray;
          color: white;
        }
      </style>
    </head>
    <body>
      <div id="container"></div>
      <script>
        window.onload = () => {
          var containerEl = document.getElementById('container');
          var i = 0;
          containerEl.addEventListener('mousemove', throttle((e) => {
            containerEl.textContent = i++
          }, 200, true));
        }
    
        /**
         * 节流
         */
        const throttle = (callback, delay, immediate) => {
          let timer, context, args;
    
          let run = () => {
            timer = setTimeout(() => {
              // 仅在不是立即模式时执行,防止二次执行
              // 当 immediate 为 true时, run方法的作用仅用于创建定时器,用于下次执行控制
              !immediate && callback.apply(context, args);
              clearTimeout(timer) // 清除定时器,
              timer = null // 回收timer,防止对后面的执行造成影响
            }, delay)
          }
    
          return function() {
            // 存储上下文和实参列表
            context = this;
            args = arguments;
            
            if (timer) return; // 当前如果有定时器任务,则取消操作
    
            !!immediate && callback.apply(context, args); // 如果需要立即执行,则执行
            run() // 再次执行run方法创建定时器用于下次判断
          }
        }
      </script>
    </body>
    </html>
    

    完整代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>节流Throttle</title>
      <style>
        #container {
          width: 200px;
          height: 200px;
          text-align: center;
          line-height: 200px;
          background-color: gray;
          color: white;
          margin-top: 20px;
        }
      </style>
    </head>
    <body>
      <input id="input" type="text">
      <span>input输入结果</span>
      <div id="container"></div>
      <script>
        window.onload = () => {
          var inputEl = document.getElementById('input');
          var containerEl = document.getElementById('container');
          var i = 0;
    
          inputEl.addEventListener('input', debounce(function(e) {
            this.nextElementSibling.textContent = this.value
          }, 1000, true));
    
          containerEl.addEventListener('mousemove', throttle((e) => {
            containerEl.textContent = i++
          }, 200, true));
        }
    
        /**
         * 防抖: 重复操作重置定时器
         */
        function debounce (callback, delay, immediate) {
          let timer, context, args, isExecuted;
    
          let run = () => {
            timer = setTimeout(() => {
              !isExecuted && callback.apply(context, args);
              clearTimeout(timer);
              timer = null;
            }, delay)
          }
    
          return function() {
            context = this;
            args = arguments;
            // 重复进入时,设置为false,用于 run内部进行判断
            // 若没有此标志位,run内只能用 immediate 来判断优化
            // 但问题是,如果使用immediate,那么定时器完成后始终不会执行callback
            isExecuted = false;
    
            // 定时器存在时,清空并重建
            // 没有必要释放timer,因为run方法会重新赋值
            if (timer) {
              clearTimeout(timer);
              run()
            } else {
              // timer不存在的两种情况
              // 1. 初始状态,还没有创建任何定时器
              // 2. 完成了一次执行,timer被释放
              // 所以当timer不存在时,需要创建;
              // timer存在时,说明还在进行计时,此时需要清除定时器并重新创建
              !!immediate && callback.apply(context, args);
              isExecuted = true; // 标记为已执行
              run();
            }
          }
        }
    
        /**
         * 节流
         */
        const throttle = (callback, delay, immediate) => {
          let timer, context, args;
    
          let run = () => {
            timer = setTimeout(() => {
              // 仅在不是立即模式时执行,防止二次执行
              // 当 immediate 为 true时, run方法的作用仅用于创建定时器,用于下次执行控制
              !immediate && callback.apply(context, args);
              clearTimeout(timer) // 清除定时器,
              timer = null // 回收timer,防止对后面的执行造成影响
            }, delay)
          }
    
          return function() {
            // 存储上下文和实参列表
            context = this;
            args = arguments;
            
            if (timer) return; // 当前如果有定时器任务,则取消操作
    
            !!immediate && callback.apply(context, args); // 如果需要立即执行,则执行
            run() // 再次执行run方法创建定时器用于下次判断
          }
        }
      </script>
    </body>
    </html>
    

    相关文章

      网友评论

          本文标题:防抖和节流

          本文链接:https://www.haomeiwen.com/subject/jukqiktx.html