web-dev-qa-db-ja.com

JavaScript setTimeoutがそれほど不正確である理由は何ですか?

私はこのコードをここで手に入れました:

var date = new Date();
setTimeout(function(e) {
    var currentDate = new Date();
    if(currentDate - date >= 1000) {
         console.log(currentDate, date);
         console.log(currentDate-date);
    }
    else {
       console.log("It was less than a second!");
       console.log(currentDate-date);
    }
}, 1000);

私のコンピューターでは、コンソール出力に1000と表示され、常に正しく実行されます。同じコードの他のコンピューターに興味があれば、タイムアウトコールバックは1秒未満で始まり、currentDate - dateは980から998の間です。

この不正確さを解決するライブラリの存在を知っています(たとえば、 Tock )。

基本的に、私の質問は次のとおりです:setTimeoutが指定された遅延で起動しないのはなぜですか?それはコンピュータである可能性があります遅すぎると、ブラウザは自動的に低速に適応しようとし、以前にイベントを発生させますか?

PS:Chrome JavaScriptコンソールで実行されたコードと結果のスクリーンショットです:

Enter image description here

37
Tomas Prado

特に正確である必要はありません。ブラウザーがコードを実行できる時間を制限する要因はいくつかあります。 [〜#〜] mdn [〜#〜] からの引用:

「クランプ」に加えて、後でページ(またはOS /ブラウザ自体)が他のタスクでビジー状態になったときにタイムアウトが発生することもあります。

言い換えると、setTimeoutが通常実装されている方法は、特定の遅延の後で実行されることを意味します。andブラウザのスレッドは自由に実行できます。

ただし、ブラウザによって実装方法が異なる場合があります。ここに私が行ったいくつかのテストがあります:

var date = new Date();
setTimeout(function(e) {
    var currentDate = new Date();
    console.log(currentDate-date);
}, 1000);

// Browser Test1 Test2 Test3 Test4
// Chrome    998  1014   998   998
// Firefox  1000  1001  1047  1000
// IE 11    1006  1013  1007  1005

Chromeからの1000回未満は、Dateタイプの不正確さに起因する可能性があります。または、Chromeが異なるコードを実行するタイミングを決定するための戦略—タイムアウトの遅延がまだ完了していない場合でも、コードを最も近いタイムスロットに収めようとしている可能性があります。

つまり、信頼性が高く、一貫性のあるミリ秒スケールのタイミングが期待できる場合は、setTimeoutを使用しないでください。

24
p.s.w.g

一般に、コンピュータープログラムは、50ミリ秒より高い精度で物事を実行しようとすると、非常に信頼性が低くなります。これは、オクタコアハイパースレッドプロセッサであっても、OSが通常数百のプロセスやスレッドを処理しているためです。 OSは、すべてのタスクを次々にCPU時間のスライスを取得するようにスケジュールすることで、すべてのマルチタスクを機能させます。

つまり、タイムアウトを1000ミリ秒に設定した場合、現在のブラウザプロセスがその時点で実行されない可能性はほとんどないため、ブラウザが1005、1010、または与えられたコールバックを実行する必要がある1050ミリ秒です。

通常、これは問題ではなく、発生しますが、それが最重要であることはめったにありません。その場合、すべてのオペレーティングシステムがカーネルレベルのタイマーを提供しますmore 1ミリ秒よりも正確であり、開発者はprecisely正しい時点でコードを実行できます。ただし、JavaScriptは、サンドボックス化された環境であるため、そのようなカーネルオブジェクトにアクセスできません。他のユーザーを飢えさせるコードを慎重に作成することにより、理論的に誰かがWebページ内からOSの安定性を攻撃できるため、ブラウザはそれらを使用しません。多くの危険なタイマーでそれを押しつぶすことによってスレッド。

テストで980が生成される理由についてはわかりません。これは、使用しているブラウザーとJavaScriptエンジンによって異なります。ただし、ブラウザがシステムの負荷や速度を手動で少しだけ下げて、「平均して遅延がまだ正しい時間である」ことを確認すると、完全に理解できます。サンドボックスの原則から、システムの残りの部分に負担をかけることなく、必要な時間を概算します。

19

この情報を誤解している場合は、誰かが私を訂正してください。

John Resigからの投稿 によると、プラットフォーム間のパフォーマンステストの不正確さについて(強調は私のものです)

システム時間は常に最後のクエリ時間(が約15ミリ秒離れている)に切り捨てられるため、パフォーマンス結果の品質は大幅に低下します。

したがって、システム時間と比較すると、両端で最大15ミリ秒のファッジがあります。

7
Mathletics

私も同じような経験をしました。
私は次のようなものを使用していました:

var iMillSecondsTillNextWholeSecond = (1000 - (new Date().getTime() % 1000));
setTimeout(function ()
{
    CountDownClock(ElementID, RelativeTime);
}, iMillSecondsTillNextWholeSecond);//Wait until the next whole second to start.

数秒ごとに1秒をスキップすることに気づきました。
しかし、私はまだそれをキャッチします。10秒または20秒後にスキップし、ガタガタに見えました。
私は、「たぶんタイムアウトが遅すぎるか、何か他のものを待っているのではないですか?」と思いました。
次に、「たぶん速すぎて、ブラウザが管理しているタイマーが数ミリ秒ずれているのではないかと気づきましたか?

変数に+1ミリ秒を追加した後、スキップするのは1回だけでした。
念のため、+ 50msを追加しました。

var iMillSecondsTillNextWholeSecond = (1000 - (new Date().getTime() % 1000) + 50);

少しハッキーですが、タイマーはスムーズに動作しています。 :)

1
MikeTeeVee