zoukankan      html  css  js  c++  java
  • JS中的函数节流throttle详解和优化

    JS中的函数节流throttle详解和优化
    在前端开发中,有时会为页面绑定resize事件,或者为一个页面元素绑定拖拽事件(mousemove),这种事件有一个特点,在一个正常的操作中,有可能在一个短的时间内触发非常多次事件绑定程序。
    DOM操作时很消耗性能的,如果你为这些事件绑定一些操作DOM节点的操作的话,那就会引发大量的计算,在用户看来,页面可能就一时间没有响应,这个页面一下子变卡了变慢了。在IE下,如果你绑定的resize事件进行较多DOM操作可能直接就崩溃了。

    怎么解决?函数节流(throttle)就是一种办法。

    函数节流的原理
    某些代码不可以在没有间断的情况连续重复执行。
    第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码。
    当第二次调用该函数时,它会清除前一次的定时器并设置另一个。
    如果前一个定时器已经执行过了,这个操作就没有任何意义。
    然而,如果前一个定时器尚未执行,其实就是将其替换为一个新的定时器。
    目的是只有在执行函数的请求停止了一段时间之后才执行。

    简单点说就是:当触发一个事件时,先setTimout让这个事件延迟一会再执行,如果在这个时间间隔内又触发了事件,那我们就clear掉原来的定时器,再setTimeout一个新的定时器延迟一会执行。
    实际使用过程中考虑到用户体验问题,会在这个基础上做一些优化。

    一个简单实现的例子:
    函数实现方式:

    function throttle(method, context) {
         clearTimeout(methor.tId);
         method.tId = setTimeout(function(){
             method.call(context);
         }, 100);
    }

    调用方法:

    window.onresize = function(){
        throttle(myFunc);
    }

    ---------------

    闭包实现方式:

    var throttle = function(fn, delay){
         var timer = null;
         return function(){
             var context = this, args = arguments;
             clearTimeout(timer);
             timer = setTimeout(function(){
                 fn.apply(context, args);
             }, delay);
         };
    };

    调用方法:

    window.onresize = throttle(myFunc, 50);
    -------------
    上面的函数节流有个问题是:只要你在50ms内不间断resize,myFunc就一次都不执行处理函数。
    对上面的节流函数做拓展:

    var throttleV2 = function(fn, delay, mustRunDelay){
         var timer = null;
         var t_start;
         return function(){
             var context = this, args = arguments, t_curr = +new Date();
             clearTimeout(timer);
             if(!t_start){
                 t_start = t_curr;
             }
             if(t_curr - t_start >= mustRunDelay){
                 fn.apply(context, args);
                 t_start = t_curr;
             }
             else {
                 timer = setTimeout(function(){
                     fn.apply(context, args);
                 }, delay);
             }
         };
    };

    拓展后的节流函数升级版,可以设置第三个参数,即必须触发执行的时间间隔。

    方法调用:
    window.onresize = throttleV2(myFunc, 50, 100);

    50ms的间隔内连续触发的调用,后一个调用会把前一个调用的等待处理掉,但每隔100ms至少执行一次。
    一开始记录第一次调用的时间戳,然后每次调用函数都去拿最新的时间跟记录时间比,超出给定的时间就执行一次,更新记录时间。

    -----------------
    例子场景2:实现常见的搜索功能
    1.没有使用函数节流的情况下,为input绑定keyup事件处理函数,在控制台输出我输入的内容。
    每按下一个键盘键,就输出了一次。短短的一些内容,输出了15次,如果每一次都是一次ajax查询请求的话就发了14个请求了。在性能上的消耗可想而知。
    假如我不断地输入,输入了很多内容,但是我每两次之间的输入间隔都小于自己设置的delay值,那么,这个queryData搜索函数就一直得不到调用。实际上,我们更希望的是,当达到某个时间值时,一定要执行一次这个搜索函数。所以,就有了函数节流的改进模式。

    函数节流增强版
    HTML:
    <input id="search" type="text" name="search">
    JS:

        <script>
            function queryData(text){
                console.log("搜索:" + text);
            }
            var input = document.getElementById("search");
            input.addEventListener("keyup", function(event){
                throttle(queryData, null, 500, this.value,1000);
                // throttle(queryData, null, 500, this.value);
                // queryData(this.value);
            });
            
            function throttle(fn,context,delay,text,mustApplyTime){
                clearTimeout(fn.timer);
                fn._cur=Date.now();  //记录当前时间
    
                if(!fn._start){      //若该函数是第一次调用,则直接设置_start,即开始时间,为_cur,即此刻的时间
                    fn._start=fn._cur;
                }
                if(fn._cur-fn._start>mustApplyTime){ 
                //当前时间与上一次函数被执行的时间作差,与mustApplyTime比较,若大于,则必须执行一次函数,若小于,则重新设置计时器
                    fn.call(context,text);
                    fn._start=fn._cur;
                }else{
                    fn.timer=setTimeout(function(){
                        fn.call(context,text);
                    },delay);
                }
            }
       </script>

    显然,连续的输入,到一定时间间隔之后,queryData函数必然会被调用,但是又不是频繁的调用。达到了节流的目的,又不会影响用户体验。

    进一步的优化可以在调用throttle函数之前,先对输入的内容进行判断,若其值为空、值不变不调用。

  • 相关阅读:
    JavaScript脚本学习
    PE文件结构 (转贴)
    Squid 代理服务器 编译源码 伪造HTTP_X_FORWARDED_FOR 请求头
    设置win2003远程桌面允许2个以上会话
    2003远程桌面声音问题
    AS3正则表达式
    Visual Studio技巧之打造拥有自己标识的代码模板
    如何重建sql数据库索引
    多线程系列(转)
    时间差
  • 原文地址:https://www.cnblogs.com/zdz8207/p/js-throttle.html
Copyright © 2011-2022 走看看