节流和防抖

160 阅读4分钟

节流

定义

高频事件触发,但在n秒内只会执行一次,所以节流会西施函数的执行频率

应用场景

举例:预定一个函数只有在大于等于执行周期时才执行,周期内调用不执行。就好像你在淘宝抢购某一件限量热卖商品时,你不断点刷新点购买,可是总有一段时间你点上是没有效果,这里就用到了节流,就是怕点的太快导致系统出现bug。

应用场景:多数在监听页面元素滚动事件的时候用到,因为滚动事件,是一个高频触发的事件,以下是监听页面元素滚动的式例代码

代码实现

// 函数节流
var canRun = true;
document.getElementById("throttle").onscroll = function(){
 if(!canRun){
 // 判断是否已空闲,如果在执行中,则直接return
 return;
 }
 canRun = false;
 setTimeout(function(){
 console.log("函数节流");
 canRun = true;
 }, 300);
};

逻辑

节流的要点:声明一个变量当标识位,记录当前代码是否在执行

如果空闲,则可以正常触发方法执行

如果代码正在执行,则取消这次执行方法,直接return。

这个方法的作用是监听ID为throttle元素的滚动事件

当canRun为true,则代表现在的滚动处理时间是空闲的,可以使用

通过关卡if(!canRun),等于就拿到了通行证。然后下一步的操作就是立马将关卡关上canRun=false。这样,其他请求执行滚动事件的方法,就被挡回去了。

接着用setTimeout规定最小的时间间隔300,接着再执行setTimeout方法体里面的内容。

最后,等setTimeout里面的方法都执行完毕,才释放关卡canRun=true,允许下一个访问者进来。

这个函数节流的实现形式,需要注意的是执行的间隔时间是>=300ms。如果具体执行的方法是包含callback的,也可以将canRun=true这一步放到callback中。理解了函数节流的关卡设置重点,其实改起来就简单多了。

防抖

定义

触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间

应用场景

举例:就好像在百度搜索时,每次输入之后都有联想词弹出,这个控制联想词的方法就不可能是输入框内容一改变就触发的,他一定是当你结束输入一段时间之后才会触发。

函数防抖的应用场景,最常见的就是用户注册时候的手机号码验证和邮箱验证了。只有等用户输入完毕后,前端才需要检查格式是否正确,如果不正确,再弹出提示语。以下还是以页面元素滚动监听的例子,来进行解析:

代码实现

// 函数防抖
var timer = false;
document.getElementById("debounce").onscroll = function(){
 clearTimeout(timer); // 清除未执行的代码,重置回初始化状态
 timer = setTimeout(function(){
 console.log("函数防抖");
 }, 300);
}; 

逻辑

函数防抖的要点,需要一个setTimeout来辅助实现。延迟执行需要跑的代码。

如果方法多次触发,则把上次记录的延迟执行代码用clearTimeout清掉,重新开始。

如果计时完毕,没有方法进来访问触发,则执行代码。

这个方法的作用是监听ID为debounce元素的滚动事件

进入滚动事件方法体的时候,做的第一件事就是清除上次未执行的setTimeout。而setTimeout的引用id由变量timer记录。

clearTimeout方法,允许传入无效的值。所以这里直接执行clearTimeout即可。

然后,将需要执行的代码放入setTimeout中,再返回setTimeout引用给timer缓存。

如果倒计时300ms以后,还没有新的方法触发滚动事件,则执行setTimeout中的代码。

函数防抖的实现重点,就是巧用setTimeout做缓存池,而且可以轻易地清除待执行的代码。

其实,用队列的方式也可以做到这种效果。这里就不深入了。

区别

防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。

防抖是一段时间内执行一次,如果这段事件内有事件执行,则重新开始计时

节流是一段时间执行一次,这个频率是非常快的,但是节流把这个频率降下来了,每隔一段时间执行一次

可直接运行的代码

<!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 class="border">
            <div class="content">普通函数</div>
        </div>
        <div class="border">
            <div class="content">防抖</div>
        </div>
        <div class="border">
            <div class="content">节流</div>
        </div>
    </body>
</html>
<style>
    .border {
        width: 100px;
        height: 100px;
        overflow: scroll;
        margin-bottom: 30px;
    }
    .content {
        border: 1px solid red;
        width: 100px;
        height: 300px;
    }
</style>
<script>
    let normal0 = document.getElementsByClassName('border')[0];
    let normal1 = document.getElementsByClassName('border')[1];
    let normal2 = document.getElementsByClassName('border')[2];
    normal0.onscroll = function () {
        console.log('普通函数');
    };
    //防抖
    var timer = null;
    normal1.onscroll = function () {
        clearTimeout(timer);
        timer = setTimeout(() => {
            console.log('防抖');
        }, 300);
    };
    //节流
    let canScroll = true;
    normal2.onscroll = function () {
        if (canScroll) {
            canScroll = false;
            setTimeout(() => {
                console.log('节流');
                canScroll = true;
            }, 300);
        }
        return;
    };
</script>