在js中如果打算使用setInterval进行倒数,计时等功能,往往是不准确的,因为setInterval的回调函数并不是到时后立即执行,而是等系统计算资源空闲下来后才会执行.而下一次触发时间则是在setInterval回调函数执行完毕之后才开始计时,所以如果setInterval内执行的计算过于耗时,或者有其他耗时任务在执行,setInterval的计时会越来越不准,延迟很厉害.
下面的代码可以说明这个问题
- var startTime = new Date().getTime();
- var count = 0;
- //耗时任务
- setInterval(function(){
- var i = 0;
- while(i++ < 100000000);
- }, 0);
- setInterval(function(){
- count++;
- console.log(new Date().getTime() - (startTime + count * 1000));
- }, 1000);
-
代码里输出了setInterval触发时间和应该正确触发时间的延迟毫秒数
- 176
- 340
- 495
- 652
- 807
- 961
- 1114
- 1268
- 1425
- 1579
- 1734
- 1888
- 2048
- 2201
- 2357
- 2521
- 2679
- 2834
- 2996
- ......
-
可以看到延迟是越来越严重的.
为了在js里可以使用相对准确的计时功能,我们可以
- var startTime = new Date().getTime();
- var count = 0;
- setInterval(function(){
- var i = 0;
- while(i++ < 100000000);
- }, 0);
- function fixed() {
- count++;
- var offset = new Date().getTime() - (startTime + count * 1000);
- var nextTime = 1000 - offset;
- if (nextTime < 0) nextTime = 0;
- setTimeout(fixed, nextTime);
-
- console.log(new Date().getTime() - (startTime + count * 1000));
- }
- setTimeout(fixed, 1000);
-
代码里,通过1000(也就是周期时间)减去当前时间和准确时间的差距,来算出下次触发的时间,从而修正了当前触发的延迟.
下面是输出
- 186
- 200
- 230
- 271
- 158
- 899
- 900
- 899
- 900
- 899
- 899
- 899
- 902
- 899
- 418
- 202
- 232
- 266
- 145
- 174
- 192
- 214
- 242
- 268
- 149
- 179
- 214
- ......
-
可以看到虽然触发时间并非绝对准确,但由于每次触发都进行及时修正,所以并没有造成误差积累.