上一篇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”。這一點在下面這份文檔中有說明:
http://www.wanfangdata.com.cn/qikan/periodical.Articles/jdgc/jdgc99/jdgc9903/990318.htm
timeSetEvent()可以通過一些API:timeBeginPeriod()來調整計時精度。這一點請參考:
http://www.vckbase.com/document/viewdoc/?id=1234
最後,我們來講結論。對於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對我提供的協助。本文基本上是資源整理貼。^.^