zoukankan      html  css  js  c++  java
  • js中的函数节流

    函数节流的目的

        从字面上就可以理解,函数节流就是用来节流函数从而一定程度上优化性能的。例如,DOM 操作比起非DOM 交互需要更多的内存和CPU 时间。连续尝试进行过多的DOM 相关操作可能会导致浏览器挂起,有时候甚至会崩溃。尤其在IE 中使用onresize 事件处理程序的时候容易发生,当调整浏览器大小的时候,该事件会连续触发。在onresize 事件处理程序内部如果尝试进行DOM 操作,其高频率的更改可能会让浏览器崩溃。又例如,我们常见的一个搜索的功能,我们一般是绑定keyup事件,每按下一次键盘就搜索一次。但是我们的目的主要是每输入一些内容搜索一次而已。为了解决这些问题,就可以使用定时器对函数进行节流。

    函数节流的原理

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

    函数节流的基本模式

    复制代码
    var processor = {
       timeoutId: null,
         //实际进行处理的方法
       performProcessing: function(){
         //实际执行的代码
       },
      //初始处理调用的方法
      process: function(){
        clearTimeout(this.timeoutId);
        var that = this;
        this.timeoutId = setTimeout(function(){
          that.performProcessing();
        }, 100);
      }
    };
    //尝试开始执行
    processor.process();
    复制代码

    好吧,看得确实有点迷糊。下面通过一个例子来详细说明,并且在这个基础模式之上做一个优化处理。

    例子场景:实现常见的搜索功能

    ①没有使用函数节流的情况下,为input绑定keyup事件处理函数,在控制台输出我输入的内容。

    测试主要代码:

    复制代码
    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){ queryData(this.value);
            });
        </script>        
    复制代码

    结果如图:

     可以看出,这种情况下,每按下一个键盘键,就输出了一次。短短的一些内容,输出了14次,如果每一次都是一次ajax查询请求的话就发了14个请求了。在性能上的消耗可想而知。

    ②使用基本的函数节流模式的情况。

    测试主要代码:

    复制代码
    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);
                // queryData(this.value);
            });
            
            function throttle(fn,context,delay,text){
                clearTimeout(fn.timeoutId);
                fn.timeoutId = setTimeout(function(){
                    fn.call(context,text);
                },delay);
            }
       </script>
    复制代码

    结果如图:

     可以看出,这种情况下,输入了好一些内容,只输出了一次,因为测试的时候设置了两次输入间隔是500ms,实际应用可根据情况设置。显然,这在性能上大大滴得到了优化。不过,这样的话,有一个新问题,如下图:

    好吧,或许看不出端倪。其实问题就是,假如我不断地输入,输入了很多内容,但是我每两次之间的输入间隔都小于自己设置的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函数之前,先对输入的内容进行判断,若其值为空、值不变都不用再调用。

     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) {
            if (!text) return;
            console.log(text);
            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);
            }
        }
    

      

  • 相关阅读:
    PHP危险函数总结学习
    2019网络与信息安全专项赛题解
    BUUCTF平台-web-边刷边记录-2
    SpringCloud-Config 分布式配置中心
    SpringCloud-Gateway 网关路由、断言、过滤
    SpringCloud-Ribbon负载均衡机制、手写轮询算法
    服务注册与发现-Eureka、Consul、Zookeeper的区别
    Docker 私有仓库搭建
    微服务熔断限流Hystrix之流聚合
    微服务熔断限流Hystrix之Dashboard
  • 原文地址:https://www.cnblogs.com/mmykdbc/p/8608780.html
Copyright © 2011-2022 走看看