对于“不用setInterval,用setTimeout”的理解

JavaScript高级程序设计(第三版)(如下简称红宝书)22.3高级定时器中详细介绍了定时器setTimeout和setInterval,看完书后,深刻理解了两者的区别,结合前辈们给个人建议“用setTimeout,不要用setInterval”,写下此文,分析这个建议的合理性。
这两个家伙看上去长得差很少,func是要执行的函数,interval是时间间隔。函数

setTimeout(func,interval)
setInterval(func,interval)

关于时间间隔,红宝书中这么说:spa

设定一个 150ms 后执行的定时器不表明到了 150ms 代码就马上执行,它表示代码会在 150ms 后被加入到队列中。若是在这个时间点上,队列中没有其余东西,那么这段代码就会被执行。

对于这个时间间隔的理解很是重要!步入正题,为什么不用setInterval,由于它可能会带来两个问题:线程

  • “丢帧”现象
  • 不一样定时器的代码的执行间隔比预期小

一图胜千言,以下图所示,让咱们跟着时间线看看这样的问题怎么发生的。假定一个场景,在click事件中设置了setInterval(func,500),假设click事件和定时器内函数的执行时间都是1s,为了方便陈述,我把不一样时间触发的func取了不一样的名字,实际上接下来的func1=func2=func3=func。在0s处触发click事件,click事件执行,在0.2s处触发定时器,0.7s处第一个函数func1加入到事件队列,但因为JS引擎是单线程的,click事件还在执行,因此func1等待着,等到1s处,click事件执行完毕,fun1才开始执行。按照定时器的时间间隔,1.2s处第二个函数func2加入到事件队列,但此时fun1正在执行,因此func2只能等待。0.5s后,也就是1.7s处,第三个函数func理应加入事件队列,可是JS引擎作了一个事情:设计

当使用 set Interval()时,仅当没有该定时器的任何其余代码实例时,才将定时器代码添加到队列中。

在1.7s处,func1在执行,func2在队列里等待执行,func2就是该定时器的代码实例,按照JS引擎的处理,func3不会加入到事件队列里,更别说执行了,这就致使出现了“丢帧”现象。而在图中也能够注意到,func1执行完毕,线程空闲了,func2就能够执行了,这就使得func1和func2之间的执行没有时间间隔,这跟咱们所预期的500ms产生一次结果是不一样的。code

setInterval遇到的问题

而若是使用链式setTimeout调用,每次函数执行的时候都会建立一个新的定时器。第二个setTimeout()调用使用了 arguments.callee 来获取对当前执行的函数的引用,并为其设置另一个定时器。这样作的好处是,在前一个定时器代码执行完以前,不会向队列插入新的定时器代码,确保不会有任何缺失的间隔。并且,它能够保证在下一次定时器代码执行以前,至少要等待指定的间隔,避免了连续的运行。代码以下所示blog

setTimeout(function(){
    //do something
    setTimeout(arguments.callee,interval);
},interval)

用setTimeout方法的话,上面假设的场景就发生了改变,以下图所示,在0s处触发click事件,click事件执行,在0.2s处触发定时器,0.7s处第一个函数func1加入到事件队列,click事件执行了1s,在1s处func1执行,2s处func1执行结束,第二个setTimeout定时器才被触发,0.5s后将函数func2加入队列,此时队列为空,func2开始执行,3.5s处func2执行结束,又一个setTimeout定时器被触发,0.5s后将函数func3加入队列,此时队列为空,func3开始执行。。。队列

setTimeout

经过上面这个场景,咱们能知道当须要用定时器来设置一个操做重复执行,而且这个操做须要执行必定的时间,记得用setTimeout,不用setInterval!事件

相关文章
相关标签/搜索