[英]setTimeout timing precision
我看到有很多關於此功能的文章,但沒有一個真正解決這個問題的:我如何確保setTimeout在這毫秒后准確而准確地運行? 我正在編寫一個對精度至關重要的應用程序,並且在很多地方(例如在這里)閱讀 ,都考慮到了我的同樣問題,
setInterval()和setTimeout()的問題是不能保證您的代碼將在指定的時間運行。
因此,有沒有辦法迫使它運行到毫秒精度? 我的應用程序實際上與我鏈接的主題的類型相同。
我已經在一個循環中運行了許多超時(我首先創建一個數組,一個數組一個接一個地運行超時),過一會兒系統不同步。
第二個問題,也許是:如果不是javascript,是否還有其他事情可能對此有所幫助? 也許要使用WebGL? 還是有一些js庫已經在考慮/解決這個問題?
編輯:我考慮使用requestAnimationFrame,但時間間隔可能會非常不同,我讀到此函數是針對恆定的步驟。
EDIT2:我在此頁面上找到了一個好的算法,這是迄今為止我能找到的最好的算法。
這是一個很好的解釋線程-通常與Javascript和瀏覽器有更多關系: JavaScript setTimeout如此不准確的原因是什么?
通過創建依賴於系統時間的自調整計時器,您可能會獲得更好的結果,但是即使那樣,延遲也可能很小(該自調整再次進行)
function doTimer(length, resolution, oninstance, oncomplete)
{
var steps = (length / 100) * (resolution / 10),
speed = length / steps,
count = 0,
start = new Date().getTime();
function instance()
{
if(count++ == steps)
{
oncomplete(steps, count);
}
else
{
oninstance(steps, count);
var diff = (new Date().getTime() - start) - (count * speed);
window.setTimeout(instance, (speed - diff));
}
}
window.setTimeout(instance, speed);
}
來源: https : //www.sitepoint.com/creating-accurate-timers-in-javascript/
Js有3個微任務隊列,分別是setTimeout/Interval/Immediate
(有人稱這些宏任務,等等), requestAnimationFrame
( rAF )和新子Promises
。 Promises盡快解決,如果setTimeouts是嵌套的(且深度超過5層),則連續調用之間的最小間隔為4ms,rAF每秒將執行約60幀。
在這些rAF中,它們知道document.hidden狀態,並且大約每17ms(理論上為16.67)執行一次。 如果所需的間隔大於此值,請使用rAF進行設置。
rAF的問題在於,因為它每隔〜17ms執行一次,所以如果我想以100 ms的間隔執行某項操作,那么在5個滴答聲之后,我將處於〜85ms,在第六個滴答聲中我將處於102ms。 我可以在102毫秒處執行,但是我需要從下一個調用時間開始降低2毫秒。 這將防止相對於您指定的幀意外地“退出”回調。 您可以大致設計一個接受選項對象的函數:
function wait(t,options){
if(!options){
options = t;
window.requestAnimationFrame(function(t){
wait(t,options);
});
return options;
}
if(!options._set) {
options.startTime = options.startTime || t;
options.relativeStartTime = options.startTime;
options.interval = options.interval || 50;
options.frame = options.frame || 0;
options.callback = options.callback || function(){};
options.surplus = options.surplus || 0;
options._set = true;
}
options.cancelFrame = window.requestAnimationFrame(function(t){
wait(t,options);
});
options.elapsed = t - options.relativeStartTime + options.surplus;
if (options.elapsed >= options.interval) {
options.surplus = options.elapsed % options.interval;
options.lastInvoked = t;
options.callback.call(options);
options.frame++;
options.relativeStartTime = t;
}
return options;
}
該對象將在每次調用時回收和更新。 將粘貼內容復制到您的控制台上,然后嘗試:
var x = wait({interval:190,callback:function(){console.log(this.lastInvoked - this.relativeStartTime)}})
回調執行與this
指向選擇對象。 返回的x是options對象本身。 要取消運行:
window.cancelAnimationFrame(x.cancelFrame);
這並不總是必須像間隔一樣,也可以像setTimeout一樣使用它。 假設您說的變量框架具有32的倍數,在這種情況下擴展options對象:
var x = wait({frameList:[32,32,64,128,256,512,1024,2048,32,128],interval:96,callback:function(){
console.log(this.lastInvoked - this.relativeStartTime);
window.cancelAnimationFrame(this.cancelFrame);
this.interval = this.frameList[this.frame];
if(this.interval){
wait(this);
}
}})
我在選項對象中添加了frameList鍵。 這是我們要執行回調的一些時間值。 我們從96開始,然后進入frameList數組,32、32、64等。如果運行上述內容,您將獲得:
99.9660000000149
33.32199999992736
33.32199999992736
66.64400000008754
133.28799999994226
249.91499999980442
517.7960000000894
1016.5649999999441
2049.7950000001583
33.330000000074506
133.31999999983236
因此,這些就是我對您遇到的情況的看法。
它的運行盡可能接近指定的時間間隔。 如果您設置非常接近的間隔(例如28、30、32),則將無法通過肉眼檢查差異。 也許嘗試使用控制台記錄“盈余”值,如下所示:
var x = wait({interval:28,callback:function(){
console.log(this.surplus);
}})
您會看到不同時間間隔的數字略有不同,並且這些數字會隨着時間推移而變化,因為我們正在防止“逐步淘汰”。 最終測試將是查看一定數量的“幀”所花費的平均時間:
var x = wait({interval:28,callback:function(){
if(this.frame === 99){
console.log(this.lastInvoked - this.startTime);
window.cancelAnimationFrame(this.cancelFrame);
}
}}) //logs something around 2800
例如,如果將間隔更改為32,它將記錄大約3200ms等。總而言之,我們設計的功能不應該取決於實時時間,它應該從js引擎獲取我們當前所在的幀,並且應該以此為基礎。
不,真的沒有辦法。 Javascript運行時環境(例如v8)沒有實時保證,也不能在實時操作系統上運行它們。
基本上,不能保證由宿主操作系統的調度程序按時調度javascript運行時,也不能保證JavaScript運行時會在指定的時間間隔精確地調度計時器回調,因為它除了處理該回調外還處理許多其他任務。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.