再谈JavaScript时钟中的16ms精度问题.

上一篇BLOG中,通过测试我们发现 JavaScript的时钟是16ms的间隔. 对于IE来说,每次总会发生16ms的间隔;对于firefox来说,会存在0ms的间隔. 对于后者,我曾解释说:可能是Java使用了自己的时钟.

先说第二种情况,对于firefox中的js引擎,我尚未去看代码,因此我只说"可能",但后来hax来说,firefox的JS引擎仍是C写的,这才想起的确如此.所以这里先说,我前面关于firefox的问题的解释是错的.

再说16ms的问题. 我其实也怀疑,为什么是16ms,而不是其它的什么值呢?

hax给了我很多信息.我这里来整理一下:

首先是取时间值.也就是我们在JavaScript中用new Date()得到时间值采用的方法其实是不准确的. 该值总会是15~16ms的间隔值,其原因在于:

Windows系统获取时间主要是用下面的几种方法
  一:GetTickCount()
      这个就是用的上篇里说的系统时钟中断
  Windows NT 3.5 及更高版本,精度为 10ms(100Hz)
  Windows NT 3.1 ,精度为 16ms(60Hz)
  Windows 95 及更高版本,精度为 55ms(18.2Hz)
  (对于Windows XP(NT5.0) 及更高版本,实测得的精度为 16ms)

  二:timeGetTime()
  精度约1ms(需通过其它API配合)

  三:High-Resolution Timer
      这种方法就使用了CPU的RTC
  QueryPerformanceCounter() 配合 QueryPerformanceFrequency(),适用于高精度应用场合

参见:http://www2.matrix.org.cn/thread.shtml?topicId=10491&forumId=1&fid=1

也就是说,如果JavaScript的new Date()采用getTickCount()来实现,那么它必然是返回16ms间隔的时间值的。

举例来说,你可能写一个超大循环用来收集一批Date()对象,最终你会发现,大多数时候是一样的值,而每两
批时间值的差值总在15~16ms.

接下说时间精度.也就是讨论setTimeout/setInterval()为什么是16ms,而另外一些人/资料会提及到10ms这个精度值.首先我们来看时钟的实现:

  一、使用系统时钟SetTimer()
  系统时钟是采用向窗体发WM_TIMER事件来激活处理例程的。而且同一个窗体消息队列中只允许同时存在一个WM_TIMER,因此它可能被丢失消息。

  二、使用timeSetEvent()
  可以使用timeSetEvent()来设置时钟。缺省情况下,他是16ms精度的。另外,同进程中同时最多有16个timeSetEvent()。

  三、在线程中使用sleep()
  Zhe曾用C代码做过测试,证明sleep()的精度是10ms。也就是说,sleep(1)产生的效果最低限也是10ms的间隔。

参见:http://dev.cbw.com/vc/progress/200510315005_4279849.shtml

timeSetEvent缺省情况下是16ms/10ms间隔的,更确切地说,是“对于Intel 芯片的精度为16ms,对于MIPS芯片的精度为10ms”。这一点在下面这份文档中有说明:

timeSetEvent()可以通过一些API:timeBeginPeriod()来调整计时精度。这一点请参考:

最后,我们来讲结论。对于JavaScript来说,由于new Date()只需要得到毫秒值,并不需要过高的精度,因此最合理的方法当然是调用GetTickCount()来取值,而不是采QueryPerformanceCounter() 配合 QueryPerformanceFrequency()来得到高精度的时间值。由于GetTickCount()自身的限制,JavaScript的new Date()得到的时间隔是16ms精度的。——对于win98或其它系统来说,这个值可能是58ms/10ms。

对于定时器来说,JavaScript应该会是使用timeSetEvent()来做定时器。因为如果用线程+sleep()会存在较大的开销(事实上我出观察到线程计数是没有变化的),而采用SetTimer()会存在消息队列的问题。所以采用timeSetEvent()是最实际的方案。——至于16个timeSetEvent()的限制,可以通过同一个timeSetEvent()中处理多个例程来规避。因此,由于timeSetEvent()自身在缺省状态下的限制,因此导致了16m时间间隔。

对于FireFox中的和WebKit(safari)中的脚本引擎,因为是开源的,有兴趣的不妨自己读读相关的代码。就不再讲述了。

感谢Zhe和hax对我提供的帮助。本文基本上是资源整理贴。^.^