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函数之前,先对输入的内容进行判断,若其值为空、值不变不调用。

  • 相关阅读:
    Codeforces 1485C Floor and Mod (枚举)
    CodeForces 1195D Submarine in the Rybinsk Sea (算贡献)
    CodeForces 1195C Basketball Exercise (线性DP)
    2021年初寒假训练第24场 B. 庆功会(搜索)
    任务分配(dp)
    开发工具的异常现象
    Telink MESH SDK 如何使用PWM
    Telink BLE MESH PWM波的小结
    [LeetCode] 1586. Binary Search Tree Iterator II
    [LeetCode] 1288. Remove Covered Intervals
  • 原文地址:https://www.cnblogs.com/zdz8207/p/js-throttle.html
Copyright © 2011-2022 走看看