setTimeout你知多少

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

无论学得多少,仍是总结一些,仍是一些小问题。固然也是很重要的,好! 废话少说该入正题了。浏览器

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

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

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?? 答案是错误的。为何?this

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

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()仍只会被调用一次。

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

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

相关文章
相关标签/搜索