JS事件中防抖debounce和节流throttle概念原理的学习
2017-03-19 20:57

前言

    从开始写博客,到现在写了80多篇文章,感觉自己会的东西也就这么多了,然而自己不会的东西却是那么那么多。所以开启了学习模式,加紧自己的学习步伐,要追上现在前端的发展步伐。一段时间不学习,前端推出的很多新东西都就不明白了。感叹前端的日新月异,真是个无底洞。真是“一入前端深似海,从此红尘是路人”。

debounce和throttle介绍

浏览器的一些事件,如:resize,scroll,keydown,keyup,keypress,mousemove等。这些事件触发频率太过频繁,绑定在这些事件上的回调函数会不停的被调用。这样浏览器的目的是为了保证信息的一致性,而对于我们来说就是一种资源的浪费了。

blob.png

弄了一个demo,查看mousemove触发了多少次。为了解决这种太频繁的事件触发,不知道是哪个外国人就整出了这么两个概念:debounce可以翻译成防抖,throttle可以翻译成节流。

debounce的作用是在让在用户动作停止后延迟x ms再执行回调。

throttle的作用是在用户动作时没隔一定时间(如200ms)执行一次回调。

他们两个的共同点就是将多次回调的触发合并成一次执行。这就大大避免了过于频繁的事件回调操作。

本质就是将事件回调函数用debounce或throttle包装,事件触发的频率没有改变,只是我们自定义的回调函数的执行频率变低了。这个处理是基于DOM操作是十分巨大的开销。所以如果你的回调函数只是处理一些js的数据,那么用不用防抖和节流处理是一样的。

debounce实现

function debounce(fn,delay){

    var delay=delay||200;
    var timer;
    return function(){
        var th=this;
        var args=arguments;
        if (timer) {
            clearTimeout(timer);
        }
        timer=setTimeout(function () {
                timer=null;
                fn.apply(th,args);
        }, delay);
    };
}

我们使用debounce包装事件回调如下:

dom.addEventListener("mousemove",debounce(function(){
    
    //做一些dom操作或者其他的。
    .....
}));

上面debounce的代码很简单,就是通过setTimeout来实现的。

我们调用debounce的时候,它运行返回一个function,这个function使用外面的局部变量delay和timer。这个返回的function才是事件执行的回调。每次执行的时候判断timer是否有值,有值则clearTimeout清空定时器,并且重新开启定时器,直到delay时间内没有触发事件时才会真正执行事件的回调。

对于这里,要插一句,对于一些前端新人们,可能对这个代码会有疑问。你只要记住,js是单线程的,就算是事件回调也跟setTimeout的运行机制是一样的,都是通过轮询的机制来顺序执行的。不存在事件和settimeout的方法同时执行的可能。肯定是一个先一个后的执行。

这个debounce的事件合并对于在用户名验证时的keydown事件上能很好的节省网络资源。

写了个keydown的debouce事件合并的demo:debounce处理keydown用户验证

当然用户验证还需要处理保证网络响应数据的顺序问题,不在本文讨论范围内。

对于mousemove在拖动效果的事件触发你可以查看:mousemove在拖动时的事件触发

blob.png

虽然触发的频率很高,但是你从每次触发的位置信息可以看出,每一次位置值都不一样,没有重复,这里就不能使用debounce进行事件合并了。

那么你可能有疑虑,那么你可以查看:debounce处理的mousemove拖动。这个例子中使用debounce方法包装了mouseove事件,整个拖动效果就不见了。就变成了跳动了。

debounce有好处,但不是处处都适用,在keydown时处理很好,能大量节省资源,在拖动中使用就有点鸡肋了。

throttle实现

function throttle(fn,interval){
    var last;
    var timer;
    var interval=interval||200;
    return function(){
        var th=this;
        var args=arguments;
        var now=+new Date();
        if(last&&now-last<interval){
            clearTimeout(timer);
            timer=setTimeout(function(){
                last=now;
                fn.apply(th,args);
            },interval);
        }else{
            last=now;
            fn.apply(th,args);
        }
    }
}

使用方法跟debounce一样。代码逻辑也类似。在触发时超过间隔时间interval ms则执行。否则不执行。if判断中的setTimeout是保证最后一次事件触发后能够调用,所以每次执行没到间隔时间时先清除timer,再重新启动timer。而在达到间隔时间时执行函数。代码逻辑也很简单,不用多说,相信聪明的你一看就能明白。

这个throttle节流的功能就是在固定的间隔时间执行回调函数,最常用的用处就是resize,scroll事件中处理。

用mousemove作为触发的例子:throttle和debounce的mousemove触发对比

blob.png

总结

学习了debounce和throttle两个方法,要知道在什么时候用他们。debounce用在keydown事件上验证用户名最好。而throttle用在resize改变布局上,onscroll滚动时候的。每天学一点,每天提高一点,积少成多,总有一天能和大神喝茶的。


原创文章,转载请注明来自:妹纸前端-www.webfront-js.com.
阅读(7419)
辛苦了,打赏喝个咖啡
微信
支付宝
妹纸前端
妹纸前端工作室 | 文章不断更新中
京ICP备16005385号-1