6

一般に、ブラウザは、最小クランプを超えても、使用する実際の時間間隔を変更する場合があるようsetIntervalです。たとえば、次のコードがあります。

function start() {
    window.setInterval(function() {
        update();
    }, 1);
}

lastTime = new Date;
numFrames = 0;
lastFrames = 0;

function update() {
    numFrames++;
    if (new Date - lastTime >= 1000) {
        lastFrames = numFrames;
        numFrames = 0;
        lastTime = new Date;
    }
}

ここでlastFramesは、約過去 1 秒間のフレーム数を示します。Chrome、Firefox、および Safari で使用すると、このコードは 1 ミリ秒で実行されません。もちろん、各ブラウザにはsetInterval呼び出し間の任意の最小時間があるため、これは予想されることです。ただし、ページが実行され続けると、タブがまだフォーカスされていても、フレーム レートは減少し続けます。これを修正するために私が見つけた唯一の方法は、ブラウザに何かをさせることです。これらの行に沿った何かが、ブラウザをsetInterval可能な限り高速に実行するようです:

function start() {
    window.setInterval(function() {
        update();
    }, 1);
}

lastTime = new Date;
numFrames = 0;
lastFrames = 0;

function update() {
    numFrames++;
    if (new Date - lastTime >= 1000) {
        lastFrames = numFrames;
        numFrames = 0;
        lastTime = new Date;
    }

    //doIntensiveLoop, processing, etc.
}

setIntervalしたがって、私の質問は次のとおりです。ブラウザは、私が求めているものに近づくことを正当化するために何を探しているのでしょうか?

編集: HTML5 仕様では、ブラウザーは setInterval を 4 ミリ秒未満の間隔で実行することを許可してはならないと規定されています。

4

5 に答える 5

5

setIntervalsetTimeoutまったく正確ではなく、そのように設計されていません。JavaScript はシングル スレッドであるため、setTimeout/setInterval基本的には「このコードのチャンクを取得して実行キューに入れます。到達したら、十分な時間が経過したら実行し、そうでない場合はキューに戻して後で再試行してください」と言います。 .

setInterval が 4 ミリ秒に設定されていて、アプリ内の実行に 10 ミリ秒かかる場合、setInterval を 4 ミリ秒で実行する方法はありません。

これがアニメーション/ゲームの目的である場合は、requestAnimationFrameを試してください。どのように実装されているかはわかりませんが、より正確なタイミングが約束されることの 1 つです。

于 2012-07-06T23:04:00.107 に答える
3

まず、Interval 関数に何を期待するかを自問する必要があると思います。

  • コンテキストを維持する必要があります。カウンターを確実にインクリメントできない間隔は、非常に悲惨です。

  • 関数内にあるものは何でも実行する必要があり、実行間隔よりも優先されます (ここでも同じです。再度インクリメントする前にタイマーを上げなければなりません)。

  • js コードの残りの部分とは別の実行スタックを持つ必要があります (残りの部分が実行を開始するまで、これらのタイマーがビジネスを終了するのを待ちたくありません)。

  • 彼らは、それがどれほど広大であっても、自分がいるコンテキストを認識している必要があります (たとえば、タイマー関数内で jQuery を使用できるようにしたい)。

したがって、Matt Greerが上で述べたように、間隔は設計上正確ではありません。これは主に、間隔が正確であると実際には期待していないが、特定の時間にコードを確実に実行することを期待しているためです。

Chromium の実装を見ると、 setTimeout と setInterval の実装がDOMTimer::installに基づいていることがわかります。これには、実行コンテキスト、アクション、およびタイマーが単発の場合が渡されます。

これは、RunloopTimer に渡され、システム タイマーの助けを借りてループを実行します(こちらを参照)。

(ちなみに、クロムは間隔に最小 10 ミリ秒をインストールします。ここで確認できます)

アクションのすべての実行はここで処理されます。これは、最後の実行が特定のタイミング制限を超えた、または下回ってから経過した時間にわたってアサーションを行いません。

反対に、タイマーのネスティング レベルが深くなりすぎないことを主張するだけです。これは、リソースを過剰に使用しているタイマーや、指定された間隔で実行が遅すぎるタイマーを遅くすることによって行われます。

if (m_nestingLevel >= maxTimerNestingLevel)
            augmentRepeatInterval(minimumInterval - repeatInterval());
    }

AugmentRepeatInterval は、間隔にミリ秒を追加するだけです。

void augmentRepeatInterval(double delta) { augmentFireInterval(delta); m_repeatInterval += delta; }

それで、私たちは何を結論付けることができますか?

  • インターバルまたはタイムアウトの時間精度を測定するのは時間の無駄です。気にする必要があり、気にすることができるのは、関数で実行したいことに対して間隔をあまり低く設定しないことです。ブラウザは、間隔とタイムアウトをタイムリーに実行するために最善を尽くしますが、正確なタイミングを保証するものではありません。

  • 間隔の実行は、環境、ブラウザー、実装、バージョン、コンテキスト、アクション自体などに依存します。正確であることを意図したものではなく、setTimeout や setInterval を使用して正確に何かをプログラムしたい場合は、今気が狂っているか、後で狂ってしまうかのどちらかです。

  • あなたのコードでは、関数に重い実行を追加すると、タイマーがより正確になったと言いました。これにはさまざまな理由が考えられます (おそらく、より多くのメモリ、より多くの排他的 CPU 時間、より多くのワーカーなどを取得します)。私はそれに非常に興味がありますが、重い実行を行ったコードをまだ提供していません。そのため、それに関する回答が必要な場合は、コードを提供してください。コードなしでは何も想定するのが難しいためです。

  • しかし、インターバルをよりタイムリーに実行することが何であれ、それは決して信頼できるものではありません. 異なるシステムで測定を開始すると、あらゆる種類のさまざまな結果が得られる可能性が高くなります。


アップデート

それ以外に、何も結果をもたらさないコードは、ブラウザの js エンジンによって適切に最適化される (実行されない、1 回だけ実行される、より適切な方法で実行される) 場合があります。あなたの例に基づいた例を挙げましょう(クロムで実行されたすべてのもの):

function start() {
  window.setInterval(function() {
    update();
  }, 1);
}

lastTime = new Date;
 numFrames = 0;
lastFrames = 0;

function update() {
  console.log(new Date() - lastTime);
  lastTime = new Date();
  for (var i=0; i < 1000000; i++) { var k = 'string' + 'string' + 'string' }
}

ヒット後の最初の実行にstartは時間がかかりますが、それ以降の実行には時間がかかりません (少なくとも Webkit では)。これは、反復内のコードが何も変更されず、ブラウザが最初の実行後にそれを認識し、単純にそれ以上実行しないためです。

実行が外部変数へのバインディングを維持する必要がある場合 (kこの場合) 、それがどのように変化するかを見てみましょう。

var k;

function update() {
  console.log(new Date() - lastTime);
  lastTime = new Date();
  for (var i=0; i < 1000000; i++) { k = 'string' + 'string' + 'string' }
}

OK、ここでは実行時間に多少の影響がありますが、それでもかなり高速です。ブラウザーは、for ループが常に同じことを行うことを認識していますが、一度だけ実行します。たとえば、反復によって実際に巨大な文字列が作成された場合はどうなるでしょうか。

var k;

function update() {
  console.log(new Date() - lastTime);
  lastTime = new Date();
  k = '';
  for (var i=0; i < 1000000; i++) { k += i.toString() }
}

何百万文字もの文字列を返さなければならないため、これはブラウザーを苦境に陥れます。それをもっと苦しめることはできますか?

var k;

function update() {
  console.log(new Date() - lastTime);
  lastTime = new Date();
  k = '';
  for (var i=0; i < 1000000; i++) { k = ['hey', 'hey', 'hey'].join('') }
}

この配列の連結は最適化できず、ほとんどすべてのブラウザをゆっくりと苦痛に満ちたものにします。

したがって、あなたの場合、重い実行により、より多くのメモリが予約され、オプティマイザによって即座に解放される可能性があります。新鮮な空気の息吹と余分なメモリとアイドル状態の CPU が関数を喜んでジャンプさせたのかもしれませんが、私が言ったように、重い実行コードを見ない限り、信頼できるものは何もありません。

于 2012-07-10T18:30:55.383 に答える
1

ブラウザがしばらくすると間隔が遅くなる理由はいくつか考えられます (これはすべて推測ですが、それはあなたが求めたものだと思います):

  1. 時間のかかるガベージ コレクションなどの実行をスケジュールする必要があります。
  2. ブラウザーは、インターバル タイマーが CPU を使いすぎているか、バッテリーを消費しすぎていると判断し、しばらくするとスロットルします。一部のブラウザーは、タブがフォーカスを失ったときにこれを行うように文書化されているため、他のときにもそうする可能性があると想像できます。
  3. ブラウザーは最初、ユーザーの間隔を優先して実行が必要な他の作業を延期しますが、しばらくすると、その作業を延期しなくなり、時間がかかります。
于 2012-07-07T00:29:04.220 に答える
0

質問:What is the browser looking for to justify running setInterval closer to what I ask it to?

回答: 何もありません。それは可能な限り速くそれを行います。速すぎる場合は、待機します。「可能な限り」は環境によって異なります。

問題:However, as the page continues to run, the frame rate will continue to decrease, even if the tab is still focused.

これはオペレーティング システムの問題です。

ページの読み込み時にコードが最初に実行されると、フレームレートとして 250 が報告され、オペレーティング システムが CPU またはメモリの割り当てをブラウザーのプロセスに制限することを決定するまで続行されます。ブラウザーは可能な限り最小間隔 (Firefox の場合は 4 ミリ秒) でこれを処理するよう求めていますが、オペレーティング システムはあまり気にしません。30 分間のランタイムの後でも、オペレーティング システムの注意を引き、フル 250 フレームレートに戻ることができます。

繰り返しますが、これはブラウザとは関係ありません。

これを「集中ループ」として入れてみて、結果がブラウザに起因する方法を説明してみてください。

for (var intense = 0; intense < 10000; intense++) {
        var dec = intense * (5039 + intense);
        for (var processing = 0; processing < intense; processing++) {
            var float = dec / (processing + 1);
        }
}

本当に素晴らしいコンピューター (ブラウザーとはまったく関係ありません)を持っていない限り、それを実行すると、CPU が泣いているのが物理的に聞こえるはずです。

于 2012-07-10T23:06:28.540 に答える