簡體   English   中英

setTimeout計時精度

[英]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 (有人稱這些宏任務,等等), requestAnimationFramerAF )和新子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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM