再談JavaScript時鐘中的16ms精度問題.

來源:互聯網
上載者:User

上一篇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對我提供的協助。本文基本上是資源整理貼。^.^

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.