防抖和节流

209 阅读3分钟

这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战

前置知识

setTimeout函数

setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式。

提示:  1000 毫秒= 1 秒。 、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、 setTimeout.png

clearTimeout函数 clearTimeout() 方法可取消由 setTimeout() 方法设置的 timeout。 clearTimeout() 方法的参数必须是由 setTimeout() 返回的 ID 值。

语法

image-20210808134440985

clearTimeout.png setInterval函数

超时值是指在指定时间之后执行代码,间歇时间值是指每隔指定的时间就执行一次代码。设置超时调用的方法是setTimeout(),设置间歇调用的方法是setInterval()。

setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。

函数防抖

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

举例:点击触发事件函数

请输入数字:<input id="normal"></input><script>
function ajax(value) {
            console.log('ajax 请求:', value);
}
let emitBussiness = document.getElementById('normal')
emitBussiness.addEventListener('keyup', function (e) {
    ajax(e.target.value)
})
</script>

没有防抖.png 这个函数的作用是每按下一个按键,就输出当前输入框里的值。

假设我们想输入一串字符,然后发送请求查询这串字符,但这串字符还没输完就发送了很多个请求,有些不符合实际情况。

接下来,我们在这个函数中加入防抖。

请输入数字:<input id="normal"></input>
    
    <script>
        function ajax(value) {
            console.log('ajax 请求:', value);
        }
        function delayAjax(fun, delay) {
            return function (val) {
                let that = this
                let args = val
                //console.log('before', fun.setId);
                clearTimeout(fun.setId)
                fun.setId = setTimeout(() => {
                                fun.call(that, val)
                            }, delay)
                //console.log('after', fun.setId);
            }
        }
        let emitBussiness = document.getElementById('normal')
        let funD = delayAjax(ajax, 1000)
        emitBussiness.addEventListener('keyup', function (e) {
            funD(e.target.value)
        })
    </script>

注意:clearTimeout(fun.setId) 这条语句的位置要在下面的setTimeout函数之前。这时因为第一次按键,fun.setId保存的值是undefined,第二次按键,fun.setId保存的值是1,第三次按键,fun.setId保存的值是2,... 也就是说,在执行setTime函数之前,fun.SetId保存的是上一次setTimeout的返回值。而我们的需求也正是当距离上一次按键的时间间隔不到指定事件就取消上一次的请求。

防抖.png 首先,按下按键1,1秒之内没有按下别的按键,输出'ajax 请求:1';按下按键2,1秒之内没有按下别的按键,输出'ajax 请求:12';接着,快速按下3、4、5、6,间隔时间小于1秒,接着等待1秒后,输出'ajax 请求:123456';可见我们的防抖起了作用。

我们加入了防抖以后,当你在频繁的输入时,并不会发送请求,只有当你在指定间隔内没有输入时,才会执行函数。如果停止输入但是在指定间隔内又输入,会重新触发计时。

函数节流

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

指当连续触发事件时,在 n 秒中只执行一次函数。

比如,我们一直喝水,但不是一喝水就上厕所,而是每隔一段时间就去上厕所。

初始html代码:(没有节流)

<!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>
    <div id="content"
        style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;width:100%">
    </div>
    <script>
        let num = 0;
        const content = document.getElementById('content');
        function count() {
            // 一些异步请求或者别的代码
            content.innerHTML = num++;
        };
        content.onmousemove = count;
    </script> 
</body>
</html>

实现1--时间戳:

<div id="content"
        style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;width:100%">
    </div>
    <script>
        // 第一次触发,最后一次不触发
        // 顾头不顾尾
        
        // 当触发事件时,执行函数时取出当前时间戳,用当前时间戳减去一开始设置的时间戳,如果时间差值>设置的时间周期,就执行函数并更新当前时间为时间戳。
        function throttle(func, wait) {
            let context, args;
            //之前的时间戳
            let old = 0;
            return function() {
                // 如果不对this做处理,func里面的this指向window,但我们要让this指向当前的div对象
                context = this
                args = arguments
                // 获取当前时间戳
                let now =  new Date().valueOf();
                if ( now - old > wait ) {
                    // 立即执行
                    func.apply(content, args)
                    old = now
                }
            }
        }

        let num = 0;
        const content = document.getElementById('content');
        function count() {
            // 一些异步请求或者别的代码
            content.innerHTML = num++;
        };
        content.onmousemove = throttle(count, 2000);
    </script> 

实现2--定时器:

<div id="content"
        style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;width:100%">
    </div>
    <script>
        // 顾尾不顾头 
        // 第一次不会触发,最后一次会触发

        // 触发事件时,我们会设置一个定时器timeout,再触发这个事件时,如果timeout存在,就不执行;直至执行后定时器设置为null。
        function throttle(func, wait) {
            let context, args, timeout;
            

            return function() {
                context = this
                args = arguments
                if(!timeout) {
                    timeout = setTimeout(() => {
                        timeout = null
                        func.apply(context, args)
                    }, wait)
                }
        
            }
        }

        let num = 0;
        const content = document.getElementById('content');
        function count() {
            // 一些异步请求或者别的代码
            content.innerHTML = num++;
        };
        content.onmousemove = count;
    </script> 

总结

  • 函数防抖和函数节流都是防止某一时间频繁触发,但是原理却不一样。
  • 函数防抖是某一段时间内只执行一次,而函数节流是间隔时间执行。

应用场景

防抖

  • search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
  • 不断的调整浏览器窗口大小会不断的触发resize事件,用防抖来让其只触发一次。

节流

  • 鼠标不断点击触发,mousedown(单位时间内只触发一次)
  • 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断 、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

参考

链接:juejin.cn/post/684490…

链接:juejin.cn/post/684490…