zoukankan      html  css  js  c++  java
  • setTimeout你知多少

    假期这么快就结束了,其实对我来说没什么影响,因为我一周才两节课,对于课多的同学来说,我天天在休假,不要羡慕哟~  但休假并不代表闲着,还是得苦逼的编代码,唉。。一入程序深似海。。

    不管学得多少,还是总结一些,还是一些小问题。当然也是很重要的,好! 废话少说该入正题了。

    上次提到异步,当时说,不知道是啥就去查汉语字典,但后来发现查了字典还是不会。回顾一下

    js哪些操作是异步的???setTimeout、setInterval、ajax、各种事件处理,才疏学浅,我就知道这些,谁还知道有哪些,劳烦告诉我,学习学习。

    for(var i=0; i<5; i++){
       setTimeout(function(){
         console.log(i);
       },100);
    }   //答案是: 5 5 5 5

    for(var i=0; i<5; i++){
        (function(j){
           setTimeout(function(){
              console.log(j);
           },100);
        })(i);  
    };   答案是 0 1 2 3 4

    为什么是这个答案,重申一遍:作用域的关系。具体解释作用域与执行环境无关,由定义时决定并一步一步往上查找。上述两个例子 执行匿名函数时执行的是:

    function(){
         console.log(i);
       }  而i等于多少? 我们从定义处查找 setTimeout中没有i  在往上一层就到了全局中,此时i已经等于5  所以答案是全是5
    function(){
         console.log(j);
       }  而j等于多少?  我们还是从定义处查找 setTimeout中没有j  在往上一层就到了上一个形参为j的匿名函数,此时j是形参,在定义setTimeout中的函数时,j的形参依次被传入实参i,依次为0,1,2,3,4  所以答案是全是0,1,2,3,4

    换汤不换药,找个例子实验一下:

    [1,2,3,4,5].forEach(function(elem){
        setTimeout(function(){console.log(elem)}, 200);
    })  答案是多少??  5,5,5,5??  1,1,1,1??  1,2,3,4,5还是???  答案是1,2,3,4,5  如果错了的话,再把前面的例子,文字看看。 

    继续,再来一道:

    for(var i=0; i<5; i++){
       (function(j){
          setTimeout(function(){
              console.log(j);
          }, Math.random()*1000);
       })(i);
    } //这个答案是什么呢??  是0,1,2,3,4还是什么???   好好想想。

    根据前面的分析,先找定义处 依次往上查找,到function(j)这个函数时,已经把实参i传进来了,所以答案是0,1,2,3,4  yes or no?? 答案是错误的。为什么?

    实参i确实把值传进来了,该段代码就等价于

    setTimeout(function(){
              console.log(0);
          }, Math.random()*1000);
    
    setTimeout(function(){
              console.log(1);
          }, Math.random()*1000);
    
    setTimeout(function(){
              console.log(2);
          }, Math.random()*1000);
    
    setTimeout(function(){
              console.log(3);
          }, Math.random()*1000);
    
    setTimeout(function(){
              console.log(4);
          }, Math.random()*1000);   

    此时只看这段代码 答案是多少??? 大家肯定会说是乱序的,跟 (Math.random()*1000) 值有关,yes 你答对了。 所以上面那个答案是乱序的0,1,2,3,4

    那么,下面那个代码呢?

    setTimeout(function(){
       console.log(15);
    },100);
    setTimeout(function(){
       console.log(5);
    },200);  

    很多人肯定会说,这还用说吗? 不用想都知道是15, 5。对,但是就这段代码而言,这个15,5   从等待到执行(此处执行时间忽略不计)一共是花了300ms还是200ms呢? 答案是200ms,为什么?刚刚开头就说过,setTimeout()函数是异步的,异步有个的特点就是并发性,在同时定义这两个函数时,他们同时在等待,放入到消息队列中,所以100ms后第一个函数放入时,第二个函数已经等了100ms,所以两个函数一共等了200ms。 总之一句话:异步具有并发性,与顺序无关(时间相同或者相近的情况下有关),与时间的快慢有关,请记住它。

    这里还要提的是:关于定时器中的时间,指的是何时将定时器的代码添加到队列中,而不是何时实际执行代码,只能表示它会尽快执行。

    如 :

    document.onclick = function(){
      setTimeout(function(){
              console.log(34);
          },250); 
    };  //如果onclick事件处理程序执行了300ms 那么定时器代码至少要在定时器设置后的300ms才会被执行,也就是34至少要在300ms后输出。

     大家马上就想到,如果是这样的话,setInterval()就会出现一种情况:在代码再次被添加到队列之前没完成执行,导致定时器代码连续运行好几次,没有停顿。幸好,js引擎够聪明,能避免这个问题,如何避免?当使用setInterval()时,仅当没有该定时器的任何其他代码实例时,才将定时器代码添加到队列中,这样确保了定时器代码加入到队列中的最小时间间隔为指定间隔,注意是添加到队列中的最小时间间隔而不是执行。但是。。这个规则有两个问题:1.某些间隔会被跳过,2.多个定时器的代码执行之间的间隔可能会比预期的少。举个例子:

    某个onclick事件处理程序使用setInterval()设置了一个200ms间隔的重复定时器,如果事件程序花了300ms多一点的时间完成,定时器也花差不多的时间,就会出现上述两个问题。

    如图:   此图来自《js高级程序设计》这本书强烈推荐阅读。。。

    我们分析一下:在5ms时创建了间隔为200ms的定时器,第一个定时器在205ms后被添加到队列中,但直到onclik执行完才执行,执行时,在405ms处第二个定时器又被添加到队列中,在605ms第三个定时器要添加到队列中,但此时第二个定时器还没被执行,所以第三个不会被添加,同时在第一个定时器执行完之后第二个立即执行,所以第一个定时器和第二个定时器执行的间隔小于200ms,其实此处就是从第一个执行结束到第二个开始执行没有间隔。

    有人可能想到这样的话js引擎并没有解决定时器代码连续运行问题,确实,但其实js引擎这种做法(在仅当没有该定时器的任何其他代码实例时,才将定时器代码添加到队列中),减少了连续的次数,不至于堆积太多。

    为了避免这2个缺点,可以使用如下模式使用链式setTimeout()调用。

    setTimeout(function(){
       //处理中
       setTimeout(arguments.callee,interval); //arguments.callee  获取对当前函数执行的引用。  

    //此处把需要处理的代码写在前面 有一个好处是 :下一个定时器一定是在前一个将要结束(此处可以之直接视为结束)才定义
    }, interval);

    每次函数执行时创建一个新的定时器,这样的好处:在前一个定时器代码执行完之前,不会向队列插入新的定时器代码,确保不会有任何缺失的间隔,而且,它可以保证在下一次定时器代码执行之前,至少要等待指定的间隔,避免了连续的运行。详细请看《js高级程序设计》这本书。

     关于setTimeout()函数还有一点就是:

    大家都知道DOM比非DOM交互要更多的内存和CPU时间,如果连续进行过多的DOM相关操作可能会导致浏览器挂起甚至崩溃。如resize事件,为了绕开这个问题我们可以使用setTimeout();

    模式如下:

    var processor = {
        timeoutId: null,
        performProcessing: function(){
          //实际执行代码 
       },
       process: function(){
    clearTimeout(this.timeoutId);
    var that = this; //保存this,因为setTimeout()中用到的函数环境总是window
    this.timeoutId = setTimeout(function(){ //timeoutId用来保存本次setTimeout的id以便下一次调用时清除
    that.performProcessing();
    }, 100);
    }
    };

    processor.process();

    时间间隔设为100ms,表示最后一次调用process()之后至少100ms后才会被调用performProcessing(),如果100ms之内调用了process()共20次,performProcessing()仍只会被调用一次。因为,在100ms之内定时器都没开始执行,调用process()只会清除前一次的,最后只剩下最后一次setTimeout()。也就是说performProcessing()仍只会被调用一次。

    这个过程叫做函数节流,基本思想是:某个代码不可以在没有间断的情况连续重复执行。今天先暂且消化这些。

    哪不对,或者要补充,推荐的强烈欢迎。。。

  • 相关阅读:
    UVALive 6909 Kevin's Problem 数学排列组合
    UVALive 6908 Electric Bike dp
    UVALive 6907 Body Building tarjan
    UVALive 6906 Cluster Analysis 并查集
    八月微博
    hdu 5784 How Many Triangles 计算几何,平面有多少个锐角三角形
    hdu 5792 World is Exploding 树状数组
    hdu 5791 Two dp
    hdu 5787 K-wolf Number 数位dp
    hdu 5783 Divide the Sequence 贪心
  • 原文地址:https://www.cnblogs.com/xiao-hong/p/3650684.html
Copyright © 2011-2022 走看看