#防抖和节流 #什么是防抖和节流

一、防抖

什么是防抖?

有这样一种情况,想象有一个表单,点击提交按钮就发送请求给服务器。如果用户在很短的时间间隔内“手抖”点击了多次,又或者是恶意点击,那么就将发送多个请求。 该行为将造成服务器额外的不必要负载。

所谓防抖,实际上就是是处理这种常见的情况的描述。

实验探究

<body>
    <button>Submit</button>
    <script>
        const btn = document.querySelector('button');
        btn.onclick = function(){
            console.log("send a request...")
        }
    </script>
</body>

该段代码,当点击Submit按钮的时候,将会触发onclick事件一次,多次点击,将多次触发。

为了解决该问题,核心就是使用一个setTimeout 定时器。

    <body>
        <button>Submit</button>
        <script>
            const btn = document.querySelector('button');
            let timer = null;
            btn.onclick = function(){
                clearTimeout(timer)
                timer = setTimeout(()=>console.log("send a request..."),500)
            }
        </script>
    </body>

这就是一个基本防抖的实现。如果不容易看明白,下面我们详细的分析。


为了便于理解,我们先将第7行注释掉:

            const btn = document.querySelector('button');
            let timer = null;
            btn.onclick = function(){
                //clearTimeout(timer)
                timer = setTimeout(()=>console.log("send a request..."),500)
            }

我们定义了一个全局变量timer用于预存setTimeout实例对象,初始值设定为null

onclick 函数被触发一次时,函数体内timer定时器立即被触发,将在500毫秒后执行console打印。

如果点击多次,由于setTimeout的异步执行特点,将会开启多个setTimeout 实例,各自的延时到500毫秒之后,依次执行打印。

打印完毕后,timer被销毁。

现在取消第7行注释:

            const btn = document.querySelector('button');
            let timer = null;
            btn.onclick = function(){
                clearTimeout(timer)
                timer = setTimeout(()=>console.log("send a request..."),500)
            }

我们以两次间隔较短的点击为例:第一次点击时,timer为null,clearTimeout(timer)即clearTimeout(null), 这并不影响,不会报错。 然后开启了一个setTimeout定时器A,等候500毫秒后执行。 但是,如果在间隔小于500毫秒内,第二次点击,定时器A 还在等候,但是我触发onclick事件时,直接就将定时器timer,即定时器A清除了,所以定时器A内的打印就不会执行,然后开启一个新的定时器B。

就这样,只要我在小于定时器中定义的时间间隔内重复点击,那么之前的定时器都将被清除掉。始终执行最后一个定时器。最大的外征特点就是,狂点按钮,但是只有松手的时候,最后那一次点击有效。

以上,就是所谓的防抖,并不复杂,但是确实很巧妙。

二、 节流

同样是为了避免频繁触发某个行为,还有一种实现方式,它和防抖的实现有所区别。

什么是节流?

关于节流,我想到了一个绝佳的例子。 就是技能cd。 如果玩过无限火力小黄毛就更容易理解了。Q键恨不得不松开。

有时候,我们希望我们能够多次触发某个行为,但是又不能让他没有间隔的疯狂触发。 那么这时候的解决方案,就叫做节流。

我希望我在技能cd好了的时候,立即可以使用该技能。 但是总不能让你无限光速连Q吧, 那叫bug。

实验探究

实际上,节流的实现也很简单。

<button>Submit</button>
<script>
    const btn = document.querySelector('button');
    let trigger = true;
    btn.onclick = function() {
        if (trigger) {
            trigger = false;
            console.log("do something...")
            setTimeout(() => {
                trigger = true;
            }, 1000)
        }

    }
</script>

简单的来讲,就是加上一个自动“开关”。

我们预先定义了一个全局的变量trigger;当点击Submit 按钮,onclick 被触发。 然后依据trigger的值,来决定是否继续执行ifthen块中的逻辑。

首次点击,由于trigger的预定义值为true,所以if then 块中的逻辑必定会被执行。但是一旦进入块,立即将trigger的值改作false (这样就实现了关闭了if的块逻辑执行的“开关”。),执行一次任务后,我们通过setTimeout 埋一个定时器,1秒后执行,其任务就是将“开关”再次打开。 那么第二次点击Submit按钮的时候, 如果定时器超过了一秒,“开关”被打开,即trigger为true,才会执行,if then 逻辑块中的任务。 否则就得等待“开关”被打开。

这样,通过“开关”的控制,就能实现,即便连续点击,但是也只能按照指定的间隔时间去触发任务。 这就是节流。

上一篇:[Vue]“TypeError: Cannot read property ‘0‘ of undefined“的解决方法


下一篇:几道面试题来看JavaScript执行机制