setTimeout的误差堆积测试和分析

开发 后端
文章主要是对setTimeout的误差堆积测试和简单分析,根据JS的引擎,setTimeout可能存在一定的时间误差,长时间的误差堆积将出现较大的偏离值,所以根据一些例子做了一个简单的模拟误差堆积测试。

前段时间做了一个团购秒杀倒计时的js展现的例子(http://www.blogjava.net/dongbule/archive/2011/12/06/365687.html),倒计时的方式主要使用setTimeout的函数进行递归实现,由于是使用setTimeout,所以根据js的引擎,这其中将可能存在一定的时间误差,一个误差不要紧,两个误差无所谓,但长时间的误差堆积将出现较大的偏离值,所以根据秒杀倒计时的例子,做了一个简单的模拟的误差堆积测试,并在各个浏览器中进行测试。
鉴于秒杀倒计时都是以一秒为单位,所以下面的例子也都以1000毫秒为计算,当然各个浏览器的实现和附加代码的编写也会造成一定的时间误差,这部分误差也合并在setTimeout的实现里面计算误差。

  1. <div id="show" style="width:500px;"></div>  
  2. <script>  
  3. var w = 1000;  
  4. var second = 0;  
  5. function showtime(){      
  6. showtime.curr=  +new Date();      
  7. setTimeout('showtime()',1000);      
  8. document.getElementById("show").innerHTML+=second +"    "+(showtime.curr-(showtime.last||showtime.curr)-1000)+"    <br/>";      
  9. showtime.last=showtime.curr;      
  10. second++;  
  11. }  
  12. showtime();  
  13. </script> 

可以看到在各个不同的浏览器在不同的秒数中出现不同的误差,当然这个跟我本机的浏览器和环境有关

下面再将这些误差值进行叠加测试

  1. <div>  
  2. <div id="show" style="width:500px;"></div>  
  3. </div>  
  4. <script>  
  5. var w = 1000;  
  6. var i = 0;  
  7. function showtime(){  
  8.     showtime.curr=  +new Date();  
  9.     f = setTimeout('showtime()',1000);  
  10.     w+=(showtime.curr-(showtime.last||showtime.curr)-1000);  
  11.     document.getElementById("show").innerHTML+=i+"   "+(showtime.curr-(showtime.last||showtime.curr)-1000)  +"    "+w+"   
  12.  
  13. <br/>";  
  14.     showtime.last=showtime.curr;  
  15.     i++;  
  16. }  
  17. showtime();  
  18. </script> 

 

IE和chrome相对较为稳定,不知道是不是我本机环境的原因,firefox出现了很多大偏差,可以选择定时清空这个误差值来处理,或是采用链式的setTimeOut()来处理。

  1. <div id="show" style="width:300px;"></div>  
  2. <script>  
  3. var w = 1000;  
  4. var i = 0;  
  5. var f ;  
  6. var t ;  
  7. var k = 0;  
  8. function showtime(){  
  9.     showtime.curr=  +new Date();  
  10.     f = setTimeout('showtime()',1000);  
  11.     w+=(showtime.curr-(showtime.last||showtime.curr)-1000);  
  12.     document.getElementById("show").innerHTML+=i+"   "+w+"  "+(showtime.curr-(showtime.last||showtime.curr)-1000)  +"    <br/>";  
  13.     showtime.last=showtime.curr;  
  14.     i++;  
  15. }  
  16. function round(){  
  17.     if(k!=0){  
  18.         clearTimeout(f);  
  19.         showtime();  
  20.         document.getElementById("show").innerHTML+="  clear<br/>";  
  21.         w=0;  
  22.     }  
  23.     setTimeout('round()',10000);  
  24.     k++;  
  25. }  
  26. showtime();  
  27. round();  
  28. </script> 

其实为什么javascript的定时器会出现所谓的不可靠或偏差的情况,这是可以理解的,最主要的问题是它们并不是所谓的线程,其实
javascript是运行于单线程的环境中,而定时器只是计划代码在未来某个执行的时间,执行的时间点是不能被保证的,因为在页面的生命周期中,不同时间可能存在其他代码,或各个浏览器的内核控制住javascript进程。

settimeout几个见解

1、setTimeOut != thread | 时间片的并发调用

2、javascript以单线程的方式运行于浏览器的javascript引擎中

3、setTimeout 只是把要执行的代码在设定的时间点插入js引擎维护的代码队列

4、setTimeout 插入代码队列并不意味着代码就会立马执行的

  1. function showtime(){  
  2. // code 1...  
  3. f = setTimeout('showtime()',200); //200毫秒后要插入执行代码对浏览器的js队列  
  4. // code 2...  

以上面面的代码为例,说说它的执行流程

Code 1 -> 200毫秒后通知浏览器有队列插入 -> Code 2 -> showtime() -> …

这个种重复递归可能会造成2个问题:

1 . 时间间隔可能小于定时调用的代码的执行时间

2 . 时间间隔或许会跳过

5ms : code1 代码执行完毕,200ms后有定时器进入队列

205ms : 定时器进入队列,code2继续

300ms : function代码结束,定时器从队列中取出,执行

405ms : 第二个定时器进入队列,第一个定时器的代码在执行中

605ms : 第三个定时器意图进入队列失败,这个点的settimeout丢失

为了避免这2个问题,可以采用链式setTimeOut()进行调用

  1. setTimeOut(function(){  
  2. code处理...  
  3. setTimeOut(arguments.callee,interval);  
  4. },interval); 

这个模式链式条用setTimeOut(),每次函数执行的时候都会创建一个新的定时器,第二个setTimeOut调用使用了arguments.callee来获取当前执行的函数引用,并为其设置另外一个定时器,这样的好处在于,在前一个定时器执行完之前不会向队列中插入新的定时器代码,确保不会有任何的确实间隔,而且它可以保证在下一次定时器代码执行前,至少等待指定的间隔,避免连续的运行。

秒杀的定时器已经通过检测正式运行了,博客记录得不好,表达不是很清楚,关于setTimeOut的函数主要参考了《JavaScript高级程序设计(第2版)》的第18章。

原文链接:http://www.blogjava.net/dongbule/archive/2012/01/10/368229.html

【编辑推荐】

  1. 一个Java程序员对2011年的回顾
  2. 用Java GUI编写的画板程序
  3. Java的动态绑定机制
  4. Java中带复选框的树的实现和应用
  5. Java网络编程菜鸟进阶:TCP和套接字入门
责任编辑:林师授 来源: 于吉吉的博客
相关推荐

2019-11-12 11:15:39

setTimeout前端代码

2023-11-01 10:18:10

自动化测试工具

2021-11-08 15:38:15

消息延迟堆积

2023-03-29 10:19:44

异步编程AsyncPromise

2022-11-08 07:36:17

RocketMQ消费者消息堆积

2024-10-16 08:36:03

2011-06-14 14:43:03

灰盒测试

2011-05-16 14:54:12

测试用例

2011-05-31 14:33:53

settimeout

2010-05-27 11:44:37

2023-09-18 16:14:35

性能测试开发

2011-02-23 11:18:48

MongoDBMySQL性能测试

2023-12-21 08:01:41

RocketMQ消息堆积

2010-03-05 10:31:24

Ubuntu PHP

2011-06-14 09:12:03

JavaScript

2021-01-13 10:51:08

PromissetTimeout(函数

2021-11-23 09:00:59

消息堆积扩容RocketMQ

2011-05-16 14:24:02

软件测试

2023-05-12 07:38:46

Python基准测试性能分析

2017-09-27 15:30:01

setTimeoutsetInterval页面
点赞
收藏

51CTO技术栈公众号