瀏覽器中實現JavaScript計時器的4種創新方式
在 Web Worker中使用無限同步循環
由于 Web Worker 本質上是Web線程,因此你可以在其中無限循環而不阻塞主線程。這使你可以訪問微秒級的時間分辨率。這對于在 Worker 中做出時間關鍵的決策是特別實用的,可以讓主線程準確的知道什么時候合適。例如:只要微秒是質數,就渲染某些東西。要訪問微秒,你可以使用 performance.now。
優點
- 微秒級分辨率。
- UI線程的成本幾乎為零。
- 利用 Web Workers 的消息傳遞設計,從UI線程角度完全異步。
- 安全結束,與 setInterval 不同,調用 worker.terminate 保證不會再收到任何消息。
引用MDN:“ Worker 的 Terminate() 方法立即終止 Worker。它不會為等待 Worker 完成里面執行的程序,而是會立即停止。”
缺點
- 即使你可以做出毫秒級的決策,但返回UI線程的消息傳遞也是異步的。你無法像在 Worker 中做出決定那樣及時渲染。
- 保持線程完全被占用。手機電池可能會好點很快。
- 需要 Web Worker 支持。
- 選項卡未聚焦時不會暫停。
使用CSS動畫處理時間事件(animationiteration)
如果創建帶有無限動畫的 div。你可以訂閱其 animationiteration 事件,并在事件 animation-duration 回調時得到通知。
優點
- 自動暫停時,標簽不在焦點。當標簽不在焦點上時,事件根本不會觸發。無需擔心調用時卡住,這些調用將在再次顯示選項卡時立即運行。
- 從 DOM 中刪除隱藏的 div 時,將自動進行清理。例如,如果你有一個可渲染時間的 React 組件,則無需在卸載時做任何事情。該 div 將被刪除,該事件將不再觸發。
- 調用邏輯很優雅:.addEventListener("animationiteration", fun)。
- 超級干凈的方法來延遲啟動計時器:animation-delay。
缺點
- 有點太聰明了,可能會使你的協作者感到困惑。
- 取決于 DOM 和 CSSOM 。其他CSS規則可能會干擾你的規則。這就是為什么我建議創建一個像這樣的任意不存在的標記的原因
。也許用CSS動畫代碼整齊地放入其中創建自定義元素?。 - 如果元素具有 display: none; 屬性,則無效。
使用SVG 標簽(SMIL動畫)
- <svg>
- <rect>
- <animate
- attributeName="rx"
- values="0;1"
- dur="1s"
- repeatCount="indefinite"
- />
- </rect>
- </svg>
如果這樣調用:animate.addEventListener('repeat', fun),你的函數將每秒被調用一次。
優點
- 即使 SVG 為 display: none;也會生效。
- 從 DOM 中刪除 SVG 時自動停止。
- 直到整頁加載才開始渲染。
- 選項卡聚焦時自動暫停。
缺點
- 有點太聰明了,可能會使你的協作者感到困惑。
- 取決于 DOM 和 CSSOM 。與上述相同的警告。其他CSS規則可能會干擾你的配置。
- IE 和 Edge (在 Chromium 之前)不受支持。
- 不準確 根據我的測試,它可能會延遲15ms。
- 直到整頁加載才開始。是的,可能是一個缺點,但是也是一個功能。
使用 Web Animations API
Web Animations API 允許你在 JavaScript 中為 DOM 元素設置動畫。
有趣的是,你可以使未渲染完的元素具有動畫效果!這使你能夠訪問純 JS (和 Web api)中的定時機制。
這是替代 setTimeout 的實現:
- function ownSetTimeout(callback, duration) {
- const div = document.createElement('div');
- const keyframes = new KeyframeEffect(div, [], { duration, iterations: 1 });
- const animation = new Animation(
- keyframes,
- document.timeline
- );
- animation.play();
- animation.addEventListener('finish', () => {
- callback();
- });
- }
很整潔,不是嗎?
優點
- 不需要DOM交互。
- 不熟悉的人容易理解。
- 標簽未聚焦時自動暫停。
缺點
- 仍然是一個建議。不要在生產中使用。
- 可怕的兼容性。可能僅適用于 Chromium。
- 還是有點違反直覺的。
- 標簽未聚焦時暫停。如果用作 setTimeout 的替代品可能會很糟糕。
- 不能間隔使用。僅 onfinish 活動可用。
- 不準確 根據我的測試,誤差 ±5ms。