美文网首页
防抖节流

防抖节流

作者: 未路过 | 来源:发表于2022-10-09 00:12 被阅读0次

    22. 防抖节流

    防抖和节流的概念其实最早并不是出现在软件工程中,防抖是出现在电子元件中,节流出现在流体流动中

    而JavaScript是事件驱动的,大量的操作会触发事件,加入到事件队列中处理。

    而对于某些频繁的事件处理会造成性能的损耗,我们就可以通过防抖和节流来限制事件频繁的发生;

    1.防抖

    175.jpg

    只有等待了一段时间再也没有事件触发之后,才会真正的执行响应函数

    2. 防抖函数的应用场景

    176.jpg

    3. 节流

    177.jpg

    在一定时间之内,无论触发了多少次操作,执行函数的频率总是固定的

    4. 节流函数的应用场景

    178.jpg

    5. 防抖案例

    179.jpg

    6.Underscore

    180.jpg 181.jpg

    underscore比较轻量级

    debance

    去抖动; 防反跳; 防抖动; 弹跳; 抖动消除;

    throttle

    n.节流阀; 节流杆; 风门; 风门杆;

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
    </head>
    
    <body>
      <input type="text">
      <script src="https://cdn.jsdelivr.net/npm/underscore@1.13.2/underscore-umd-min.js"></script>
      <script>
        const inputEl = document.querySelector("input")
        let counter = 0;
        const inputChange = function() {
          console.log(`发送了第${++counter}次网络请求`);
        }
     //不进行防抖节流处理
     // inputEl.oninput = inputChange;
    
     //防抖处理
     //inputEl.oninput = _.debounce(inputChange, 2000)
    
     //节流处理
     inputEl.oninput= _.throttle(inputChange, 2000)
      </script>
    </body>
    
    </html>
    

    7.自定义防抖和节流函数

    182.jpg

    1.基本实现

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <style>
        div {
          height: 100px;
          width: 100px;
        }
      </style>
    </head>
    
    <body>
      <input type="text" id="myinput">
      <div id="myDiv"></div>
      <script>
      
        const input = document.querySelector("#myinput");
        const mydiv = document.querySelector("#myDiv");
    
        let counter = 0;
        input.oninput = debounce(myFunc, 1000);
       
        function myFunc() {
          console.log(`发送了第${++counter}次网络请求`);
        //   mydiv.innerHTML = this.value;//发送请求
        }
    
      
        //在1s之内有没有被执行,被执行的话,再延迟1s,没有的话,就执行
        //主要就是演出多久执行这个函数
    
        function debounce(fn, delay) {
          // 1.定义一个定时器, 保存上一次的定时器
          let timer = null;
    
          // 2.真正执行的函数
          function _debounce(){
    
            // 取消上一次的定时器
            if(timer) {
           clearTimeout(timer)
         }
    
         // 延迟执行
          timer = setTimeout(() => {
            // 外部传入的真正要执行的函数
            fn()
          }, delay)
          }
          return _debounce
        }
       
      </script>
    </body>
    
    </html>
    

    2. 优化参数和this指向

    有两个问题

    1执行这个函数的this指向的不是input这个dom元素

    2是返回的这个函数应该可以传入参数

    _debounce这个函数的this应该指向的是input这个元素

    因为相当于input.oninput = _debounce,

    执行fn的时候,因为是独立调用,所以fn这个函数的this指向的是window,我们得使用apply等方法给他绑定this。

    如果这里的settiemout是正常函数,可以通过bind绑定

            // 延迟执行
            timer = setTimeout(function () {
              fn.apply(this)
            }.bind(this), delay)
          }
    

    如果使用箭头函数的话,箭头函数不绑定this,就会找外层作用域的this

        // 延迟执行
            timer = setTimeout(() => {
              fn.apply(this)
            }, delay)
          }
    

    然后,关于event事件,

      input.oninput = myFunc;
    
        function myFunc(e) {
          console.log(e);
          console.log(`发送了第${++counter}次网络请求`);
          //   mydiv.innerHTML = this.value;//发送请求
        }
    

    fn是可以传入参数的,这个时候,就需要接受参数

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <style>
        div {
          height: 100px;
          width: 100px;
        }
      </style>
    </head>
    
    <body>
      <input type="text" id="myinput">
      <div id="myDiv"></div>
      <script>
    
        const input = document.querySelector("#myinput");
        const mydiv = document.querySelector("#myDiv");
    
        let counter = 0;
        input.oninput = debounce(myFunc, 1000);
    
    
        function myFunc(e) {
          console.log(e);
          console.log(`发送了第${++counter}次网络请求`);
          //   mydiv.innerHTML = this.value;//发送请求
        }
    
    
        //在1s之内有没有被执行,被执行的话,再延迟1s,没有的话,就执行
        //主要就是演出多久执行这个函数
    
        function debounce(fn, delay) {
          // 1.定义一个定时器, 保存上一次的定时器
          let timer = null;
    
          // 2.真正执行的函数
          function _debounce(...arg) {
    
            // 取消上一次的定时器
            if (timer) {
              clearTimeout(timer)
            }
              
            // 延迟执行
            timer = setTimeout(() => {
              fn.apply(this, arg)
            }, delay)
          }
          return _debounce
        }
    
      </script>
    </body>
    
    </html>
    

    3.优化立即执行效果(第一次立即执行)

    我们希望发送第一次的时候立即发送网络请求,其他后面的该延迟的就延迟。

    比如用户一直连着输入,这时候timout一直取消,不会发送任何网络请求,这样不好,就是用户按下第一个以后就马上发第一个,展示点信息,不然用户以为搜索框不起作用。

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <style>
        div {
          height: 100px;
          width: 100px;
        }
      </style>
    </head>
    
    <body>
      <input type="text" id="myinput">
      <div id="myDiv"></div>
      <script>
    
        const input = document.querySelector("#myinput");
        const mydiv = document.querySelector("#myDiv");
    
        let counter = 0;
        input.oninput = debounce(myFunc, 1000, true);
    
    
        function myFunc(e) {
          console.log(e);
          console.log(`发送了第${++counter}次网络请求`);
          //   mydiv.innerHTML = this.value;//发送请求
        }
    
    
        //在1s之内有没有被执行,被执行的话,再延迟1s,没有的话,就执行
        //主要就是演出多久执行这个函数
    
        function debounce(fn, delay, immediate = false) {
          // 1.定义一个定时器, 保存上一次的定时器
          let timer = null;
          let first = true;
    
          // 2.真正执行的函数
          function _debounce(...arg) {
            if( first && immediate) {
              fn.apply(this, arg);
              first = false
            }
    
            // 取消上一次的定时器
            if (timer) {
              clearTimeout(timer)
            }
    
            // 延迟执行
            timer = setTimeout(() => {
              fn.apply(this, arg);
              first = true;
            }, delay)
          }
          return _debounce
        }
    
      </script>
    </body>
    
    </html>
    

    4. 优化取消操作(增加取消功能)

    如果有一个按键,在输入完1s之内,它按了取消按钮,这个请求不发送

    函数对象上增加方法。

    搜索框后面有时候有一个取消键,就是我们设置的timeout时间很长,用户按下取消键的时候,我们可以取消发送网络请求。

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <style>
        div {
          height: 100px;
          width: 100px;
        }
      </style>
    </head>
    
    <body>
      <input type="text" id="myinput">
      <div id="myDiv"></div>
      <button>取消</button>
      <script>
    
        const input = document.querySelector("#myinput");
        const mydiv = document.querySelector("#myDiv");
        const myButton = document.querySelector("button")
        let counter = 0;
        const debounceChange = debounce(myFunc, 2000);
        input.oninput = debounceChange;
        myButton.onclick = function () {
          debounceChange.cancle()
        }
    
    
        function myFunc(e) {
          console.log(e);
          console.log(`发送了第${++counter}次网络请求`);
          //   mydiv.innerHTML = this.value;//发送请求
        }
    
    
        //在1s之内有没有被执行,被执行的话,再延迟1s,没有的话,就执行
        //主要就是延迟多久执行这个函数
    
        function debounce(fn, delay, immediate = false) {
          // 1.定义一个定时器, 保存上一次的定时器
          let timer = null;
          let first = true;
    
          // 2.真正执行的函数
          function _debounce(...arg) {
            if (first && immediate) {
              fn.apply(this, arg);
              first = false
            }
    
            // 取消上一次的定时器
            if (timer) {
              clearTimeout(timer)
            }
    
            // 延迟执行
            timer = setTimeout(() => {
              fn.apply(this, arg);
              first = true;
              timer = null;
            }, delay)
          }
    
          //封装取消功能 
          //函数本身也是对象,可以给函数添加属性
          _debounce.cancle = function () {
            if (timer) {
              clearTimeout(timer);
              timer = null;
              first = true;
            }
          }
    
          return _debounce
        }
    
    
    
      </script>
    </body>
    
    </html>
    

    5.优化返回值

    如果发送网络请求的响应函数有返回值呢?就是真正执行的myFunc如果有返回值的化,

    有两种方法,1种是给函数在添加一个参数,callback

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <style>
        div {
          height: 100px;
          width: 100px;
        }
      </style>
    </head>
    
    <body>
      <input type="text" id="myinput">
      <div id="myDiv"></div>
      <button>取消</button>
      <script>
    
        const input = document.querySelector("#myinput");
        const mydiv = document.querySelector("#myDiv");
        const myButton = document.querySelector("button")
        let counter = 0;
        const debounceChange = debounce(myFunc, 2000, false, (res) => {
          console.log("我是返回值" + res);
        });
        input.oninput = debounceChange;
        myButton.onclick = function () {
          debounceChange.cancle()
        }
    
    
        function myFunc(e) {
          console.log(e);
          console.log(`发送了第${++counter}次网络请求`);
          //   mydiv.innerHTML = this.value;//发送请求
          return "abc"
        }
    
    
        //在1s之内有没有被执行,被执行的话,再延迟1s,没有的话,就执行
        //主要就是延迟多久执行这个函数
    
        function debounce(fn, delay, immediate = false, resultCallback) {
          // 1.定义一个定时器, 保存上一次的定时器
          let timer = null;
          let first = true;
    
          // 2.真正执行的函数
          function _debounce(...arg) {
            if (first && immediate) {
              const result = fn.apply(this, arg);
                first = false
                
              if (resultCallback && typeof resultCallback === "function") 
                  resultCallback(result)
            }
    
            // 取消上一次的定时器
            if (timer) {
              clearTimeout(timer)
            }
    
            // 延迟执行
            timer = setTimeout(() => {
              const result = fn.apply(this, arg);
                  first = true;
                  timer = null;
              if (resultCallback && typeof resultCallback === "function")        
                  resultCallback(result)
                  
            }, delay)
          }
    
          //封装取消功能 
          //函数本身也是对象,可以给函数添加属性
          _debounce.cancle = function () {
            if (timer) {
              clearTimeout(timer);
              timer = null;
              first = true;
            }
          }
    
          return _debounce
        }
    
    
      </script>
    </body>
    
    </html>
    

    第二种方法就是使用promise

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <style>
        div {
          height: 100px;
          width: 100px;
        }
      </style>
    </head>
    
    <body>
      <input type="text" id="myinput">
      <div id="myDiv"></div>
      <button>取消</button>
      <script>
    
        const input = document.querySelector("#myinput");
        const mydiv = document.querySelector("#myDiv");
        const myButton = document.querySelector("button")
        let counter = 0;
        const debounceChange = debounce(myFunc, 2000, false);
    /*     const tempCallback = () => {
          console.log(this);//window
         
        } */
    
        const tempCallback = function(e){
          console.log(this);
          debounceChange.apply(this, [e]).then((res) => {
            console.log("Promise的返回值结果",res);
          })
    
        }
        input.oninput = tempCallback;
    
        myButton.onclick = function () {
          debounceChange.cancle()
        }
    
    
        function myFunc(e) {
          console.log(e);
          console.log(this);
          console.log(`发送了第${++counter}次网络请求`);
          //   mydiv.innerHTML = this.value;//发送请求
          return "abc"
        }
    
    
        //在1s之内有没有被执行,被执行的话,再延迟1s,没有的话,就执行
        //主要就是延迟多久执行这个函数
    
        function debounce(fn, delay, immediate = false) {
        
          // 1.定义一个定时器, 保存上一次的定时器
          let timer = null;
          let first = true;
    
          // 2.真正执行的函数
          const  _debounce = function(...arg) {
            console.log("_debounce this :", this);
            return new Promise((resolve, reject) => {
              if (first && immediate) {
              const result = fn.apply(this, arg);
              resolve(result)
              first = false
            }
    
            // 取消上一次的定时器
            if (timer) {
              clearTimeout(timer)
            }
    
            // 延迟执行
            timer = setTimeout(() => {
              const result = fn.apply(this, arg);
              resolve(result)
              first = true;
              timer = null;
            }, delay)
            })
          }
    
          //封装取消功能 
          //函数本身也是对象,可以给函数添加属性
          _debounce.cancle = function () {
            if (timer) {
              clearTimeout(timer);
              timer = null;
              first = true;
            }
          }
    
          return _debounce
        }
    
    
    
      </script>
    </body>
    
    </html>
    

    8. 自定义节流函数

    在一定时间之内,无论触发了多少次操作,执行函数的频率总是固定的。

    比如每10s执行一次,但是abcd输入完以后还没到10s。

    先实现abcd输入完是第7秒,但是不执行,只有到第10s再执行。

    这个时候距离最后一次输入距离3s之后再执行函数。如果输入完d是第10s,那么就马上执行。我们就就可以达到下面的公式。remaintime是10-(7-0)= 3,10-(10-0)=0

    大于0的化就代表要等多久执行,小于0 或者等于0,就代表马上执行。

    const remainTime = interval - (nowTime - startTime)

    interval来自于传递来的函数,throttle函数。throttle(fn, interval)

    nowTime = newDate().getTime()就是当前时间。我这次input的时候的这个当前时间。

    first就是第一次是0,再第一次触发节流,发送网络事件以后,first就变成了interval。

    [图片上传失败...(image-44b898-1665312315624)]

    1. 基本实现

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <style>
          div {
            height: 100px;
            width: 100px;
          }
        </style>
      </head>
    
      <body>
        <input type="text" id="myinput" />
        <div id="myDiv"></div>
        <button>取消</button>
       <!--  <script src="https://cdn.jsdelivr.net/npm/underscore@1.13.2/underscore-umd-min.js"></script> -->
        <script>
          const input = document.querySelector("#myinput");
          let counter = 0;
    
          function myFunc() {
            console.log(`发送了第${++counter}次网络请求`);
          }
    
          // input.oninput = _.throttle(myFunc, 10000);
          //输入第一个的时候马上执行,然后没到interval时间之前等待执行。
          //getTime()返回距 1970 年 1 月 1 日之间的毫秒数:
          input.oninput = throttle(myFunc, 3000);
    
          function throttle(fn, interval) {
            //1.记录上一次执行函数的时间,也就是开始时间
            let lastTime = 0;
            const _throttle = function () {
              //2.事件触发的时候真正执行的函数
    
              //2.1获取当前事件触发时的时间
              const nowTime = new Date().getTime();
    
              //2.2 使用当前触发时间和时间间隔和上一次开始的时间,计算出还剩余多长时间需要去触发函数
              const remainTime = interval - (nowTime - lastTime);
              if (remainTime <= 0) {
                //2.3 真正出发函数
                fn();
                //2.4保留上次触发的时间
                lastTime = nowTime;
              }
            };
            return _throttle;
          }
        </script>
      </body>
    </html>
    
    

    再这个函数里。第一次输入框的时候,函数是立刻执行的 ,因为第一次的lasttime是0,也就是gettime是一个很大的值,那个remainTime = 3000 - (很大的值 - 0);一定是负数,所以一定会执行。

    但是还有一个很大的bug就是如果最后一次输入的时间没有达到interval的时间,这个时候函数是不执行的。

    就是我用3s输入完了,但是interval是10s,我等了7s,函数还是没有执行。

    总结:

    我们可以添加3个功能

    第一个是第一次输入内容的时候触发不,leading

    第二个参数是最后一次触发不触发,就是刚才的哪个bug

    第三次是添加个一个callback,就是执行完函数之后,函数有返回值的情况。

    我们把这些参数都放到options这个对象里面。

    2. 优化立即执行效果

     if (lastTime === 0 && leading === false) {
                lastTime = nowTime;
              }
    

    html

    这个第一次是第一次在框里面输入东西。如果间隔10s,第一下输入东西

    ,函数执行。然后第11s输入东西,函数马上执行,这个就不输入与这个第一次的范畴了。而是本身的基本实现里面就有。因为remainTime小于0 了。

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <style></style>
      </head>
    
      <body>
        <input type="text" id="myinput" />
        <script>
          const input = document.querySelector("#myinput");
          let counter = 0;
    
          function myFunc() {
            console.log(`发送了第${++counter}次网络请求`);
          }
    
          input.oninput = throttle(myFunc, 2000, { leading: false });
    
          function throttle(
            fn,
            interval,
            options = {
              leading: true,
              trading: true,
            }
          ) {
            const { leading, trading } = options;
            //1.记录上一次执行函数的时间,也就是开始时间
            let lastTime = 0;
            const _throttle = function () {
              //2.事件触发的时候真正执行的函数
    
              //2.1获取当前事件触发时的时间
              const nowTime = new Date().getTime();
    
              //只有第一次的时候才会执行,因为只有第一次lastTime是0,才能造成很大的负数。
              //不想第一次触发的化,就让第一次在输入框输入内容时候的time为当前时间,而不是为0
              if (lastTime === 0 && leading === false) {
                lastTime = nowTime;
              }
    
              //2.2 使用当前触发时间和时间间隔和上一次开始的时间,计算出还剩余多长时间需要去触发函数
              const remainTime = interval - (nowTime - lastTime);
              if (remainTime <= 0) {
                //2.3 真正出发函数
                fn();
                //2.4保留上次触发的时间
                lastTime = nowTime;
              }
            };
            return _throttle;
          }
        </script>
      </body>
    </html>
    
    

    3.优化节流最后一次也可以执行

    比如10s的interval,第3s的时候输入完了,需要等7s后执行。

    这个时候就是设置一个timeout,时间设置为7

    setTimeout(fn(), 7000)

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <style></style>
      </head>
    
      <body>
        <input type="text" id="myinput" />
        <script>
          const input = document.querySelector("#myinput");
          let counter = 0;
    
          function myFunc() {
            console.log(`发送了第${++counter}次网络请求`);
          }
    
          input.oninput = throttle(myFunc, 3000, { leading: true, trading: true });
    
          function throttle(
            fn,
            interval,
            options = {
              leading: false,
              trading: true,
            }
          ) {
            const { leading, trading } = options;
    
            //1.记录上一次执行函数的时间,也就是开始时间
            let lastTime = 0;
            let timer = null;
            const _throttle = function () {
              //2.事件触发的时候真正执行的函数
    
              //2.1获取当前事件触发时的时间
              const nowTime = new Date().getTime();
    
              //只有第一次的时候才会执行,因为只有第一次lastTime是0,才能造成很大的负数。
              //不想第一次触发的化,就让第一次在输入框输入内容时候的time为当前时间,而不是为0
              //不想第一次执行就设置下面
              if (lastTime === 0 && leading === false) {
                lastTime = nowTime;
              }
    
              //2.2 使用当前触发时间和时间间隔和上一次开始的时间,计算出还剩余多长时间需要去触发函数
              const remainTime = interval - (nowTime - lastTime);
    
              if (remainTime <= 0) {
                if (timer) {
                  clearTimeout(timer);
                  timer = null;
                }
                //2.3 真正出发函数
                fn();
                //2.4保留上次触发的时间
                lastTime = nowTime;
                return;
              }
              //我们只需要一个定时器就可以了,让它记住要给我们执行fn。如果再intrval之前再也没有在输入框输入内容的化。
    
              if (trading && !timer) {
                timer = setTimeout(() => {
                  fn();
                  timer = null;
                  //这里不能直接使用nowTime,因为我们需要的是执行settimeout函数的时候的时间,而不是设置settimeout的时间。
                  //  lastTime = nowTime;
                  lastTime = new Date().getTime();
                }, remainTime);
              }
            };
            return _throttle;
          }
        </script>
      </body>
    </html>
    
    

    4. 优化参数和this指向

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <style></style>
      </head>
    
      <body>
        <input type="text" id="myinput" />
        <script>
          const input = document.querySelector("#myinput");
          let counter = 0;
    
          function myFunc(event) {
            console.log(`发送了第${++counter}次网络请求`);
            console.log(this);
            console.log(event.target.value);
          }
    
          input.oninput = throttle(myFunc, 3000, { leading: true, trading: true });
    
          function throttle(
            fn,
            interval,
            options = {
              leading: false,
              trading: true,
            }
          ) {
            const { leading, trading } = options;
    
            let lastTime = 0;
            let timer = null;
            const _throttle = function (...args) {
              const nowTime = new Date().getTime();
    
              if (lastTime === 0 && leading === false) {
                lastTime = nowTime;
              }
    
              const remainTime = interval - (nowTime - lastTime);
    
              if (remainTime <= 0) {
                if (timer) {
                  clearTimeout(timer);
                  timer = null;
                }
                fn.apply(this, args);
                lastTime = nowTime;
                return;
              }
    
              if (trading && !timer) {
                timer = setTimeout(() => {
                  fn.apply(this, args);
                  timer = null;
                  lastTime = new Date().getTime();
                }, remainTime);
              }
            };
            return _throttle;
          }
        </script>
      </body>
    </html>
    
    

    5.优化添加取消功能

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <style></style>
      </head>
    
      <body>
        <input type="text" id="myinput" />
        <button id="btn">取消</button>
        <script>
          const input = document.querySelector("#myinput");
          const btn = document.querySelector("#btn");
          let counter = 0;
    
          function myFunc(event) {
            console.log(`发送了第${++counter}次网络请求`);
            console.log(this);
            console.log(event.target.value);
          }
          const _throttle = throttle(myFunc, 3000, {
            leading: true,
            trading: true,
          });
          input.oninput = _throttle;
    
          btn.onclick = function () {
            console.log("取消发送请求");
            _throttle.cancle();
          };
    
          function throttle(
            fn,
            interval,
            options = {
              leading: false,
              trading: true,
            }
          ) {
            const { leading, trading } = options;
    
            let lastTime = 0;
            let timer = null;
            const _throttle = function (...args) {
              const nowTime = new Date().getTime();
    
              if (lastTime === 0 && leading === false) {
                lastTime = nowTime;
              }
    
              const remainTime = interval - (nowTime - lastTime);
    
              if (remainTime <= 0) {
                if (timer) {
                  clearTimeout(timer);
                  timer = null;
                }
                fn.apply(this, args);
                lastTime = nowTime;
                return;
              }
    
              if (trading && !timer) {
                timer = setTimeout(() => {
                  fn.apply(this, args);
                  timer = null;
                  lastTime = new Date().getTime();
                }, remainTime);
              }
            };
            _throttle.cancle = function () {
              if (timer) {
                clearTimeout(timer);
              }
              timer = null;
              lastTime = 0;
            };
            return _throttle;
          }
        </script>
      </body>
    </html>
    
    

    5.优化返回值问题

    1.使用callback函数

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <style></style>
      </head>
    
      <body>
        <input type="text" id="myinput" />
        <button id="btn">取消</button>
        <script>
          const input = document.querySelector("#myinput");
          const btn = document.querySelector("#btn");
          let counter = 0;
    
          function myFunc(event) {
            console.log(`发送了第${++counter}次网络请求`);
            console.log(this);
            console.log(event.target.value);
            return "i am return value";
          }
          const _throttle = throttle(myFunc, 3000, {
            leading: true,
            trading: true,
            resultCallback: (res) => {
              console.log("resultCallback:", res);
            },
          });
          input.oninput = _throttle;
    
          btn.onclick = function () {
            console.log("取消发送请求");
            _throttle.cancle();
          };
    
          function throttle(
            fn,
            interval,
            options = {
              leading: false,
              trading: true,
            }
          ) {
            const { leading, trading, resultCallback } = options;
            //如果传来的options里面没有resultCallback的化,这时候解构出来的resultCallback就是undefined
    
            let lastTime = 0;
            let timer = null;
            const _throttle = function (...args) {
              const nowTime = new Date().getTime();
    
              if (lastTime === 0 && leading === false) {
                lastTime = nowTime;
              }
    
              const remainTime = interval - (nowTime - lastTime);
    
              if (remainTime <= 0) {
                if (timer) {
                  clearTimeout(timer);
                  timer = null;
                }
                const result = fn.apply(this, args);
                if (resultCallback && typeof resultCallback === "function") {
                  resultCallback(result);
                }
                lastTime = nowTime;
                return;
              }
    
              if (trading && !timer) {
                timer = setTimeout(() => {
                  const result = fn.apply(this, args);
                  if (resultCallback && typeof resultCallback === "function") {
                    resultCallback(result);
                  }
    
                  timer = null;
                  lastTime = new Date().getTime();
                }, remainTime);
              }
            };
            _throttle.cancle = function () {
              if (timer) {
                clearTimeout(timer);
              }
              timer = null;
              lastTime = 0;
            };
            return _throttle;
          }
        </script>
      </body>
    </html>
    
    

    2.使用promise

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <style></style>
      </head>
    
      <body>
        <input type="text" id="myinput" />
        <button id="btn">取消</button>
        <script>
          const input = document.querySelector("#myinput");
          const btn = document.querySelector("#btn");
          let counter = 0;
    
          function myFunc(event) {
            console.log(`发送了第${++counter}次网络请求`);
            console.log(this);
            console.log(event.target.value);
            return "i am return value";
          }
          const _throttle = throttle(myFunc, 3000, {
            leading: true,
            trading: true,
            resultCallback: (res) => {
              console.log("resultCallback:", res);
            },
          });
          const tempCallback = function (...args) {
            console.log(this); //input这个元素
            _throttle.apply(this, args).then((res) => {
              console.log(res);
            });
          };
          input.oninput = tempCallback;
    
          btn.onclick = function () {
            console.log("取消发送请求");
            _throttle.apply(this).cancle();
          };
    
          function throttle(
            fn,
            interval,
            options = {
              leading: false,
              trading: true,
            }
          ) {
            const { leading, trading, resultCallback } = options;
            //如果传来的options里面没有resultCallback的化,这时候解构出来的resultCallback就是undefined
    
            let lastTime = 0;
            let timer = null;
            const _throttle = function (...args) {
              return new Promise((resolve, reject) => {
                const nowTime = new Date().getTime();
    
                if (lastTime === 0 && leading === false) {
                  lastTime = nowTime;
                }
    
                const remainTime = interval - (nowTime - lastTime);
    
                if (remainTime <= 0) {
                  if (timer) {
                    clearTimeout(timer);
                    timer = null;
                  }
                  const result = fn.apply(this, args);
                  resolve(result);
                  lastTime = nowTime;
                  return;
                }
    
                if (trading && !timer) {
                  timer = setTimeout(() => {
                    const result = fn.apply(this, args);
                    resolve(result);
    
                    timer = null;
                    lastTime = new Date().getTime();
                  }, remainTime);
                }
              });
            };
            _throttle.cancle = function () {
              if (timer) {
                clearTimeout(timer);
              }
              timer = null;
              lastTime = 0;
            };
            return _throttle;
          }
        </script>
      </body>
    </html>
    
    

    相关文章

      网友评论

          本文标题:防抖节流

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