精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

字節面試官問粉絲,如何實現準時的SetTimeout

開發 開發工具
想得到準確的,我們第一反應就是如果我們能夠主動去觸發,獲取到最開始的時間,以及不斷去輪詢當前時間,如果差值是預期的時間,那么這個定時器肯定是準確的,那么用 while 可以實現這個功能。

[[394615]]

最近一個粉絲去面字節,被面試官問到了這個問題來問我,一聽感覺有點意思,于是對它進行了一番研究,可能研究的過程以及結果不一定是最好的,但是還是記錄一下,為各位提供一些幫助。

拿到這個問題,假設有這樣的場景,我們需要用 setTimeout 做一個動畫,并且需要控制他的頻率,50ms 運行一次,首先我們先上圖,來看看 setTimeout 的表現。

運行代碼如下,通過一個計數器來記錄每一次 setTimeout 的調用,而設定的間隔 * 計數次數,就等于理想狀態下的延遲,通過以下例子來查看我們計時器的準確性。

  1. function timer() { 
  2.    var speed = 50, // 設定間隔 
  3.    counter = 1,  // 計數 
  4.    start = new Date().getTime(); 
  5.     
  6.    function instance() 
  7.    { 
  8.     var ideal = (counter * speed), 
  9.     real = (new Date().getTime() - start); 
  10.      
  11.     counter++; 
  12.     form.ideal.value = ideal; // 記錄理想值 
  13.     form.real.value = real;   // 記錄真實值 
  14.  
  15.     var diff = (real - ideal); 
  16.     form.diff.value = diff;  // 差值 
  17.  
  18.     window.setTimeout(function() { instance(); }, speed); 
  19.    }; 
  20.     
  21.    window.setTimeout(function() { instance(); }, speed); 
  22. timer(); 

而我們如果在 setTimeout 還未執行期間加入一些額外的代碼邏輯,再來看看這個差值。

  1. ... 
  2. window.setTimeout(function() { instance(); }, speed); 
  3. for(var x=1, i=0; i<10000000; i++) { x *= (i + 1); } 
  4. ... 

可以看出,這大大加劇了誤差。

可以看到隨著時間的推移, setTimeout 實際執行的時間和理想的時間差值會越來越大,這就不是我們預期的樣子。類比真實的場景,對于一些倒計時以及動畫來說都會造成時間的偏差都是不理想的。

那么,從這個現象來看一下,為什么 setTimeout 會不準時呢?

因為我們的代碼往往并不是只有一個 setTimeout,大多數會遇到以下情況。

詳細要從瀏覽器的事件循環講起,但是講事件循環的文章太多了,文本就不再累贅地詳細展開講解。

視頻

  • https://www.youtube.com/watch?v=8aGhZQkoFbQ

(國內視頻 https://www.bilibili.com/video/av456657611/)

建議看國外的中英對照字幕,國內的翻譯準確度一般

相關文章

  • https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

總結來說,因為瀏覽器頁面是有消息隊列和事件循環來驅動的,創建一個 setTimeout 的時候是將它推進了一個隊列,并沒有立即執行,只有本輪宏任務執行完,才會去檢查當前的消息隊列是否有有到期的任務。

接下來我會用 4 這種方式來探索。

while

想得到準確的,我們第一反應就是如果我們能夠主動去觸發,獲取到最開始的時間,以及不斷去輪詢當前時間,如果差值是預期的時間,那么這個定時器肯定是準確的,那么用 while 可以實現這個功能。

理解起來也很簡單:

代碼如下:

  1. function timer(time) { 
  2.     const startTime = Date.now(); 
  3.     while(true) { 
  4.         const now = Date.now(); 
  5.         if(now - startTime >= time) { 
  6.             console.log('誤差', now - startTime - time); 
  7.             return
  8.         } 
  9.     } 
  10. timer(5000); 

打印:誤差 0

顯然這樣的方式很精確,但是我們知道 js 是單線程運行,使用這樣的方式強行霸占線程會使得頁面進入卡死狀態,這樣的結果顯然是不合適的。

Web Worker

那么既然無法在當前主線程避免這個誤差,我們能否另開一個線程去處理呢?當然可以,JavaScript 也提供給我們這樣一個能力,通過 Web Worker 我們就可以在另一個線程來運行我們的代碼。

Web Worker為Web內容在后臺線程中運行腳本提供了一種簡單的方法。線程可以執行任務而不干擾用戶界面。 -- 摘自MDN

一個 worker 的簡單的示例

  1. // main.js 
  2. var myWorker = new Worker('worker.js'); 
  3.  
  4. // 監聽 worker 
  5. myWorker.onmessage = function(e) { 
  6.   result.textContent = e.data; 
  7.   console.log('Message received from worker'); 
  8. first.onchange = function() { 
  9.   // 向 worker 發送數據 
  10.   myWorker.postMessage([first.value,second.value]); 
  11.   console.log('Message posted to worker'); 
  1. // worker.js 
  2. onmessage = function(e) { 
  3.   // 接受主線程的數據 
  4.   console.log('Message received from main script'); 
  5.   var workerResult = 'Result: ' + (e.data[0] * e.data[1]); 
  6.   console.log('Posting message back to main script'); 
  7.   // 向主線程發送數據 
  8.   postMessage(workerResult); 

那么接下來我們就要加 worker 和 while 相結合,以下為創建 worker 部分

  1. // worker生成器 
  2. const createWorker = (fn, options) => { 
  3.     const blob = new Blob(['(' + fn.toString() + ')()']); 
  4.     const url = URL.createObjectURL(blob); 
  5.     if (options) { 
  6.         return new Worker(url, options); 
  7.     } 
  8.     return new Worker(url); 
  9. }  
  10. // worker 部分 
  11. const worker = createWorker(function () { 
  12.     onmessage = function (e) { 
  13.         const date = Date.now(); 
  14.         while (true) { 
  15.             const now = Date.now(); 
  16.             if(now - date >= e.data) { 
  17.                 postMessage(1); 
  18.                 return
  19.             } 
  20.         } 
  21.     } 
  22. }) 

我們通過在 worker 中寫入一個 while 循環,當達到我們的預取時間的時候,再向主線程發送一個完成事件,就不會因為主線程的其他代碼的干擾而造成數據不準的情況。

  1. let isStart = false
  2. function timer() { 
  3.     worker.onmessage = function (e) { 
  4.        cb() 
  5.         if (isStart) { 
  6.             worker.postMessage(speed); 
  7.         }  
  8.     } 
  9.     worker.postMessage(speed); 

我們來看一下實際的效果。

我們可以看到執行的時間和理想的時間非常相近,而那細微的差異應該就是線程通訊耗時。

我們再來看看加入額外的代碼邏輯的情況。

  1. ... 
  2. if (isStart) { 
  3.    worker.postMessage(speed); 
  4. for (var x = 1, i = 0; i < 10000000; i++) { x *= (i + 1); } 
  5. ... 

![](https://s3.qiufengh.com/blog/2021-04-20 23.16.44.gif)

時間明顯增加了一些,但是增加速度非常緩慢。

雖然我們用 Web Worker 修復時間看似被解決了。但是一方面, worker 線程會被 while 給占用,導致無法接受到信息,多個定時器無法同時執行,另一方面,由于 onmessage 還是屬于事件循環內,如果主線程有大量阻塞還是會讓時間越差越大,因此這并不是個完美的方案。

requestAnimationFrame

先來看看他的定義

window.requestAnimationFrame() 告訴瀏覽器——你希望執行一個動畫,并且要求瀏覽器在下次重繪之前調用指定的回調函數更新動畫。該方法需要傳入一個回調函數作為參數,該回調函數會在瀏覽器下一次重繪之前執行,回調函數執行次數通常是每秒60次,也就是每16.7ms 執行一次,但是并不一定保證為 16.7 ms。

我們也可以嘗試一下將它來模擬 setTimeout。

  1. // 模擬代碼 
  2. function setTimeout2 (cb, delay) { 
  3.     let startTime = Date.now() 
  4.     loop() 
  5.    
  6.     function loop () { 
  7.       const now = Date.now() 
  8.       if (now - startTime >= delay) { 
  9.         cb(); 
  10.         return
  11.       } 
  12.       requestAnimationFrame(loop) 
  13.     } 

發現由于 16.7 ms 間隔執行,在使用間隔很小的定時器,很容易導致時間的不準確。

再看看額外代碼的引入效果。

  1. ... 
  2.  window.setInterval2(function () { instance(); }, speed); 
  3. for (var x = 1, i = 0; i < 10000000; i++) { x *= (i + 1); } 
  4. ... 

略微加劇了誤差的增加,因此這種方案仍然不是一種好的方案。

setTimeout 系統時間補償

這個方案是在 stackoverflow 看到的一個方案,我們來看看此方案和原方案的區別

原方案

setTimeout系統時間補償

當每一次定時器執行時后,都去獲取系統的時間來進行修正,雖然每次運行可能會有誤差,但是通過系統時間對每次運行的修復,能夠讓后面每一次時間都得到一個補償。

  1. function timer() { 
  2.    var speed = 500, 
  3.    counter = 1,  
  4.    start = new Date().getTime(); 
  5.     
  6.    function instance() 
  7.    { 
  8.     var real = (counter * speed), 
  9.     ideal = (new Date().getTime() - start); 
  10.      
  11.     counter++; 
  12.  
  13.     var diff = (ideal - real); 
  14.     form.diff.value = diff; 
  15.  
  16.     window.setTimeout(function() { instance(); }, (speed - diff)); // 通過系統時間進行修復 
  17.  
  18.    }; 
  19.     
  20.    window.setTimeout(function() { instance(); }, speed); 

再來看看加入額外的代碼邏輯的情況。

依舊非常的穩定,因此通過系統的時間補償,能夠讓我們的 setTimeout 變得更加準時,至此我們完成了如何讓 setTimeout 準時的探索。

好了我們最后來總結一下4種方案的優缺點

while Web Worker requestAnimationFrame setTimeout 系統時間補償

  while Web Worker requestAnimationFrame setTimeout 系統時間補償
準確度
主線程阻塞 阻塞 一般 不阻塞 不阻塞
評分 ⭐️⭐️ ⭐️⭐️⭐️ ⭐️ ⭐️⭐️⭐️⭐️⭐️

我們下期再見~

參考

https://segmentfault.com/q/1010000013909430

 

https://stackoverflow.com/questions/196027/is-there-a-more-accurate-way-to-create-a-javascript-timer-than-settimeout

 

責任編輯:武曉燕 來源: 秋風的筆記
相關推薦

2021-02-07 21:16:04

字節跳動面試字符串

2021-12-25 22:31:10

MarkWord面試synchronize

2021-11-08 09:18:01

CAS面試場景

2021-01-21 07:53:29

面試官Promis打印e

2024-09-11 22:51:19

線程通訊Object

2023-11-20 10:09:59

2024-02-20 14:10:55

系統緩存冗余

2015-08-13 10:29:12

面試面試官

2021-12-16 18:38:13

面試Synchronize

2025-03-10 03:00:00

CSSline字體

2024-12-25 15:44:15

2024-02-04 10:08:34

2024-01-19 14:03:59

Redis緩存系統Spring

2024-01-26 13:16:00

RabbitMQ延遲隊列docker

2024-04-09 10:40:04

2024-10-22 16:39:07

2021-12-02 18:20:25

算法垃圾回收

2020-07-28 00:58:20

IP地址子網TCP

2025-06-03 07:05:00

Linux操作系統Windows

2021-01-06 05:36:25

拉鏈表數倉數據
點贊
收藏

51CTO技術棧公眾號

欧美成人三级在线| 中文字幕欧美激情一区| 97国产真实伦对白精彩视频8| 白丝女仆被免费网站| 粉嫩91精品久久久久久久99蜜桃| 亚洲欧美在线观看| 精品一区日韩成人| 进去里视频在线观看| 羞羞色午夜精品一区二区三区| 精品国产乱码久久久久久老虎| 国产精品69页| 欧美xxxx性xxxxx高清| 免费看国产片在线观看| 欧美一级网址| 午夜精品久久久久久久久久久| 欧美性大战久久久久| 国产精品高潮呻吟av| 亚洲免费在线| 欧美日韩爱爱视频| 激情高潮到大叫狂喷水| 亚洲激情77| 欧美精品一区二区在线播放| 中文字幕第38页| 香蕉成人app免费看片| 中文一区在线播放| 欧美一区二区三区在线免费观看| 亚洲精品成人区在线观看| 日韩激情视频网站| 欧美亚洲国产日本| 五月天婷婷网站| 国产探花在线免费观看| 欧美极品免费| 激情成人中文字幕| 久久手机在线视频| 国产在线一区二区视频| 欧美国产1区2区| 久久人人九九| 亚洲欧美日韩成人在线| av电影在线观看一区| 亚洲最大福利网| 国产精品久久久久久免费播放| 久久一区精品| 日本久久中文字幕| 精品国产一区二区三区四| 一区二区三区四区五区精品视频| 久久久久久久久久国产精品| 久久久久久久久97| 欧美福利网址| 久久久免费观看| 久久免费精彩视频| 韩国自拍一区| 欧美激情区在线播放| 欧美成人免费观看视频| 欧美日韩p片| 九九热这里只有在线精品视| 日韩在线观看视频一区二区| 欧美暴力喷水在线| 欧美激情精品久久久久| 国产精彩视频在线| 99精品国产一区二区青青牛奶| 91国在线精品国内播放| 婷婷激情五月网| 免费精品视频| 国产精品视频99| 国产精品一区二区av白丝下载| 国产一区二区免费在线| ts人妖另类在线| 欧美一区二区黄片| 久久亚洲综合色一区二区三区| 欧美日韩大片一区二区三区| 在线观看免费高清完整| 亚洲乱码中文字幕| 秋霞无码一区二区| 成人国产精选| 欧美一级黄色录像| 无遮挡aaaaa大片免费看| 狠狠操综合网| 久久亚洲精品中文字幕冲田杏梨| 草视频在线观看| 亚洲黄色视屏| 国产精品成人在线| 国产婷婷一区二区三区久久| 无码人妻黑人中文字幕| 一区二区小说| 久久精品视频导航| 日韩三级免费看| 青娱乐精品在线视频| 99在线看视频| 经典三级在线| 亚洲精品国产一区二区精华液 | 色琪琪久久se色| 超薄丝袜一区二区| 一级免费在线观看| 蜜臀精品久久久久久蜜臀| av资源一区二区| 成人综合影院| 亚洲一卡二卡三卡四卡无卡久久 | 国产一区二区三区四| 狠狠色狠狠色综合人人| 色影院视频在线| 精品日本高清在线播放| 午夜视频在线观| 天堂俺去俺来也www久久婷婷 | 日韩精品最新在线观看| 在线中文字幕电影| 欧美午夜宅男影院| 亚洲观看黄色网| 一区二区三区网站 | 欧美韩国日本综合| 亚洲精品无人区| 日韩激情电影免费看| 日韩你懂的电影在线观看| 国产精品成人无码免费| 国产日韩欧美一区| 成人区精品一区二区| 亚洲图片88| 日本乱人伦aⅴ精品| 中文字幕在线永久| 欧美激情第二页| 成人精品一区二区三区电影免费 | 久久精品网站视频| 凹凸成人在线| 欧美国产第二页| 国产精品女同一区二区| 欧美精彩视频一区二区三区| 日本不卡在线观看视频| 成人知道污网站| 美日韩精品免费观看视频| 中文字幕在线观看你懂的| www欧美成人18+| 日韩在线综合网| 国产精品自在线拍| 久久久久久久激情视频| 丰满人妻一区二区| 亚洲一区在线观看网站| 色姑娘综合天天| 五月久久久综合一区二区小说| 国产精品扒开腿做爽爽爽男男| 日韩私人影院| 欧美性猛交xxxx富婆弯腰| 日韩Av无码精品| 黄色日韩精品| 国产视频在线观看一区| 狼人综合视频| 亚洲欧美国内爽妇网| 91视频免费网址| 久久综合色一综合色88| 久久久久免费精品| 亚洲制服欧美另类| 日本精品久久电影| 国产免费永久在线观看| 在线亚洲一区二区| 久久一级免费视频| 激情欧美一区二区三区在线观看| 99热这里只有精品7| 999久久久国产999久久久| 久久亚洲精品一区二区| 欧美一级淫片aaaaaa| 全球av集中精品导航福利| 亚洲第一网站免费视频| 国产香蕉视频在线| www精品美女久久久tv| 无码少妇一区二区三区芒果| 奇米影视亚洲| 亚洲最大成人网色| av在线理伦电影| 日韩激情视频在线| 中文字幕久久久久| 亚洲女同ⅹxx女同tv| 在线看黄色的网站| 久热精品在线| 视频一区二区视频| 国产伦精品一区二区三区在线播放| 欧美一级高清免费| av网站无病毒在线| 精品国产亚洲在线| 波多野结衣视频在线看| 日韩美女视频19| 日批在线观看视频| 欧美aaaaaa午夜精品| 国产精品三级一区二区| 亚洲免费毛片| 亚洲一区二区三区毛片| 国产乱码午夜在线视频| 自拍偷拍亚洲一区| 色婷婷av一区二区三| 欧美天天综合网| 日韩精品成人一区| 国产精品国产三级国产| 亚洲天堂美女视频| 精一区二区三区| www.中文字幕在线| 91超碰成人| 蜜桃传媒视频第一区入口在线看| 亚洲国产综合在线观看| 91精品国产一区| 成人免费看片| 日韩经典第一页| 国产精品久久777777换脸| 欧美视频免费在线| 久草视频在线资源| 国产精品乱码人人做人人爱| 亚洲精品乱码久久| 国产精品一区二区在线看| 国产男女激情视频| 精品999网站| 欧美日韩亚洲国产成人| av资源久久| 欧美18视频| 精品福利一区| 91视频免费进入| 99欧美精品| 日本一本a高清免费不卡| 欧美人与性动交α欧美精品济南到| 国产亚洲欧洲黄色| 日本一区二区三区在线观看视频| 日韩精品中午字幕| 国产精品自拍电影| 欧美日韩在线播放一区| 一区二区三区网址| 欧美aaaaa性bbbbb小妇| 欧美大奶子在线| 黄色软件在线| 亚洲美女激情视频| 午夜视频福利在线| 亚洲第一福利网| 亚洲成人中文字幕在线| 日韩一区二区三区电影| 国产美女免费视频| 欧美丰满嫩嫩电影| 影音先锋国产在线| 欧美日韩免费在线视频| 波多野结衣黄色网址| 色激情天天射综合网| 欧美另类一区二区| 亚洲成av人在线观看| 国产乱国产乱老熟300| 一区二区三区四区中文字幕| 印度午夜性春猛xxx交| 亚洲视频一二三区| 国产成人综合在线视频| 亚洲天堂a在线| 久久久久久久久久久久久女过产乱| 亚洲人妖av一区二区| h色网站在线观看| 一区二区三区在线免费观看| 青青草偷拍视频| 亚洲综合999| 99久在线精品99re8热| 欧美日韩国产在线播放| 天堂网视频在线| 在线观看日韩电影| 夜夜狠狠擅视频| 日韩视频在线观看一区二区| 午夜美女福利视频| 日韩高清免费在线| 免费福利在线观看| 日韩中文字幕网站| 国产激情在线视频| 久久久亚洲天堂| 一区一区三区| 国产精品自在线| 精品91福利视频| 国产亚洲欧美一区二区| 免费久久精品| 一区二区视频在线免费| 欧美亚韩一区| 18禁免费无码无遮挡不卡网站 | 亚洲成在人线免费| 一道本在线观看| 国产精品国产三级国产有无不卡| 国产suv一区二区三区| 午夜精品久久久久久久99水蜜桃| 国产精品人人人人| 欧美另类变人与禽xxxxx| 午夜精品久久久久久久99| 日韩av在线天堂网| 免费大片黄在线| 韩剧1988免费观看全集| 成人开心激情| 99视频免费观看| 亚洲资源网站| 亚洲欧美日韩不卡| 国产欧美午夜| 久国产精品视频| 97久久精品人人澡人人爽| 国产视频123区| 亚洲大片精品永久免费| 亚洲天天综合网| 亚洲精品久久7777777| 亚洲精品传媒| 538国产精品视频一区二区| va天堂va亚洲va影视| 麻豆精品视频| 中文无码久久精品| 欧美交换配乱吟粗大25p| 久久午夜精品| 日批免费观看视频| 国产精品美女久久久久aⅴ| 91香蕉在线视频| 日韩欧美在线一区二区三区| 国产中文字幕在线视频| 久久久久久国产三级电影| 男女啪啪999亚洲精品| 久久av一区二区三区亚洲| 天天综合久久| www.日日操| av亚洲精华国产精华精华 | 午夜欧美在线| 少妇激情一区二区三区| 91香蕉视频黄| 国产真人真事毛片| 91麻豆精品国产91久久久久 | 网曝91综合精品门事件在线| 三上悠亚免费在线观看| 秋霞午夜av一区二区三区| 老司机福利av| 亚洲成人7777| 亚洲国产欧美另类| 久久夜色精品国产| 欧洲午夜精品| 日韩欧美精品在线不卡| 久久一二三四| 亚洲做受高潮无遮挡| 天天操天天干天天综合网| 亚洲免费不卡视频| 欧美大荫蒂xxx| 视频精品一区| 好吊色视频988gao在线观看| 精品在线亚洲视频| 三级黄色在线观看| 精品视频1区2区3区| av福利精品| 国产欧美精品日韩| 色777狠狠狠综合伊人| 蜜臀av免费观看| 日本一区二区高清| 中文字幕在线播放不卡| 日韩在线播放视频| 亚洲精品tv| 在线播放国产一区二区三区| 偷偷操不一样的久久| 亚洲国产精品成人av| 日本不卡免费高清视频在线| 久久久精品国产一区二区三区| 国产精品久久久久9999高清| 亚洲一区二区乱码| 欧美性精品220| 岛国最新视频免费在线观看| 国产精品久久久久久久久粉嫩av| 欧洲福利电影| 97人人爽人人| 亚洲综合视频在线| 香蕉视频911| 国产精品美女www| 香蕉久久网站| yjizz视频| 欧美午夜性色大片在线观看| 可以在线观看的黄色| 国产狼人综合免费视频| 偷偷www综合久久久久久久| 国产无套精品一区二区三区| 午夜久久久久久电影| 色久视频在线播放| 国产精品网红直播| 你懂的国产精品永久在线| 美女露出粉嫩尿囗让男人桶| 亚洲精品卡一卡二| 精品av综合导航| 经典三级一区二区| 国产奶头好大揉着好爽视频| 成人精品一区二区三区中文字幕| 在线观看亚洲欧美| 丝袜一区二区三区| 999久久精品| 久久婷婷国产91天堂综合精品| 综合欧美亚洲日本| 天堂av在线免费观看| 国产精品最新在线观看| 在线日韩电影| 欧美aaa级片| 精品久久久久香蕉网| 婷婷激情一区| 国产欧美久久久久| 日本一二三不卡| 亚洲精品字幕在线| 国产91在线视频| 中国成人一区| 色欲狠狠躁天天躁无码中文字幕| 日韩欧美一级精品久久| 欧美一区久久久| 丰满少妇大力进入| 中文字幕中文字幕在线一区| 五月婷婷丁香花| 91黄在线观看| 美女一区二区三区| 国产精品久免费的黄网站| 欧美性xxxx极品hd满灌| 中文字幕一区二区三区人妻不卡| 精品视频色一区|