你的位置:首页 > Java教程

[Java教程]关于JavaScript定时器我的一些小理解


因为自己在平时工作中,有些功能需要用到定时器,但是定时器并不像我们表边上看到的那样,所以这周末我看看书查查资料,深入研究了一下JavaScript中的定时器,那么废话不多说,下面进入我们今天的正题。

大家都知道JavaScript是单线程的,所以不管是定时器还是用户的操作都是需要在线程队列中排队执行的。

一、定时器在执行线程队列里的分析

为了更好的理解我还是直接写个测试代码来看一下,这样分析起来更直观一些

 1 <script type="text/javascript"> 2   var time1 = new Date().getTime(); 3   console.log("阶乘函数开始时间:"+time1); 4   function factorial(num) { 5     if(num <= 1) { 6       return 1; 7     } else { 8       return num * arguments.callee(num - 1);  9     }10   }11   var result = factorial(150);12   console.log("阶乘函数结果:"+result);13   var time2 = new Date().getTime();14   console.log("阶乘函数结束时间1:"+time2);15   console.log("阶乘函数所用时间:"+(time2-time1));16   setTimeout(function(){17     var time3 = new Date().getTime();18     factorial(10000);19     var time4 = new Date().getTime();20     console.log("setTimeout被执行时间:"+time3);21     console.log("setTimeout执行的差值时间:"+(time3-time1));22     console.log("setTimeout里面的函数运行时间:"+(time4-time3));23   },10);24   setInterval(function(){25     var time5 = new Date().getTime();26     console.log("setInterval被执行时间:"+time5);27     console.log("setInterval执行的差值时间:"+(time5-time1));28   },10);29 </script>

 在上面这段测试代码里面一共分为三个部分:

1.阶乘计算

2.setTimeout

3.setInterval

一般人们的认知是先开始执行阶乘计算,然后10ms之后会运行setTimeout里面的内容,并且每个10ms会触发setInterval。表面上看是这样,但是事实并不一定是这样的。

下面我贴出代码的运行结果来逐一的分析

从运行结果可以看出,阶乘函数运行需要花费15ms(多次刷新页面可能这个时间会不一样,所以我们先姑且认为这个阶乘函数执行需要15ms)。

最开始的时候会启动一个10ms延迟的setTimeout定时器和一个10ms间隔的setInterval定时器,由于第一阶段的阶乘函数需要花15ms,所以在10ms的时候,前面两个定时器都已经过期了,由于JavaScript的单线程,这两个定时器就需要进入线程队列进行等待,直到第一段阶乘函数运行完。

所以可以看到setTimeout在15ms的时候才开始执行,这就是我要说的定时器即使指定了时间不一定就能决定函数何时被执行。通过打印的结果可以看到setTimeout执行完需要5ms,所以在20ms的时候setInterval被开始执行。

我这里讲的是三段函数在线程队列里的开始被执行的时间,而不是函数最后执行完的时间。

二、setTimeout和setInterval之间的区别

还是直接上代码来进行对比

1   setTimeout(function cb(){2     console.log("setTimeout");3     setTimeout(cb,10);4   },10);5   setInterval(function(){6     console.log("setInterval");7   },10);

 上面的代码看上去功能似乎是一样的,实际上两者是有区别的,在setTimeout里要等里面的函数执行完(也就是前一个callback)再延迟10ms才可以再次执行回调函数,而setInterval是每隔10ms就会去执行函数里面的内容,而不去管上一个是否执行完。这就是两者之间的区别。

三、定时器延迟时间的可靠性

看了我第一部分贴出的运行结果之后,细心的人可能会发现我打印的定时器延迟会有1-2ms的偏差,就是下面我要看的延迟时间可靠性的问题了

废话不多说直接上测试代码(为了测试效果这里我极端一点把延迟时间设置为1ms,这样数据结果会更加明显一点)

1   var time1 = new Date().getTime();2   setInterval(function(){3     var time2 = new Date().getTime();4     console.log("setInterval执行的差值时间:"+(time2-time1));5   },1);

 常理上来讲运行的结果应该是1 2 3 4 5 6 8 9 ...

但是实际的运行结果如下:

可以看到平均的延迟时间大概在5ms左右,我是拿chrome浏览器测试的,据说不同的系统下不同的浏览器这个平均的时间都不一样,如果爱钻牛角尖的小伙伴可以去尝试一下,这里就不深入展开了.

所以说定时器的延迟时间不宜设置过小,因为太小的话可能根本也达不到你想要的效果(不排除一些真可以控制在1ms左右的牛逼浏览器),而且根据设备硬件或者浏览器的不同可能延迟时间也会有少量的误差

四、定时器的小妙用

有人会说定时器能有什么妙用,无外乎就是在手机翻转屏的时候延迟几秒获取设备宽度等等。

我想说的不是这些而是利用定时器来提高性能的办法。

首先我们来模拟一个js要动态创建十万个DOM节点的场景,这种情况浏览器会花费大量的时间来执行,从而阻塞了其他代码的执行。这时如果我们使用定时器把这十万个DOM打散分成多个部分,这样一下就好了很多。

五、合理管理定时器

大家都知道定时器,用完之后需要清除,如果不清楚同时多个定时器在一个页面上跑,会损耗性能让页面浏览起来有卡顿感,尤其是定时器在动画里面的应用,想象一下,如果制作一个动画效果里面用了许多定时器,并且多个定时器同时运行,那么有可能就会出现本来后面要执行的一个动画效果在前面就被提前执行了,这是我们不想看到的。

所以我们就要根据情况对定时器做一个合理的管理,还是拿做动画为例,

1.动画肯定要保持同一时间只执行一个定时器;

2.并且自己可以灵活的控制定时器的开启和关闭。

在网上可以找到管理定时器的示例代码,大家可以参考一下:

 1   var timers = { 2     timerID : 0,  3     timers : [],    4     add : function (fn) { 5       this.timers.push(fn); 6     }, 7     start : function (){ 8       if(this.timerID) return; 9       (function runNext(){10         if(timers.timers.length>0){11           for(var i=0;i<timers.timers.length;i++){12             if(timers.timers[i]() === false){13               timers.timers.splice(i,1);14               i--;15             }16           }17           timers.timerID = setTimeout(runNext,10);18         }19       })();20     },21     stop : function(){22       clearTimeout(this.timerID);23       this.timerID = 0;24     }25   };

View Code

 其实管理的核心还是我上面提到的两条。

总结

1.如果无法立即执行定时器,定时器会进入线程队列当中排队,等待下一个可执行的时间点,所以说定时器有些时候可能要比设定的时间要长,但是不会比设定的时间少的;

2. setTimeout和setInterval在被触发的定义上是有很大区别的;

3.定时器的延迟时间不宜设置过小;

4.利用定时器可以分解大量操作的代码

5.合理管理页面中的定时器

祝大家清明小长假快乐!(其实也就比平时周末多一天而已)

感谢大家的观看,有什么分析的不对的地方欢迎大家批评指出,如果喜欢本文,请点击右下角的推荐哦~