10

Windows システムのアップタイムが 49.7 日に近づくと、内部の Windows ミリ秒ティック カウンターは 2^32 に近づきます。Internet Explorer 8 のバグにより、setInterval または setTimeout イベントをいつ起動するかを計算するときに算術オーバーフローが発生するようです。たとえば、稼働時間が 49 日目で、次のように電話するとします。

setInterval(func, 86400000); // fire event in 24 hours

関数は 24 時間後ではなく、すぐに呼び出されます。

setInterval または setTimeout に十分な数が渡された場合、このバグはおそらく 25 日間のアップタイム (2^31 ミリ秒) 後にいつでも発生します。(ただし、49日目にしかチェックしていません。)

コマンドラインで「net statistics server」と入力すると、稼働日数を確認できます。

回避策はありますか?

4

3 に答える 3

5

ラッパーを使用してバグを回避できますsetTimeout

function setSafeTimeout(func, delay){
    var target = +new Date + delay;
    return setTimeout(function(){
        var now = +new Date;
        if(now < target) {
            setSafeTimeout(func, target - now);
        } else {
            func();
        }
    }, delay);
}

これは引き続き値を返すsetTimeoutため、バグが発生しなければclearTimeout引き続き使用できます。防弾である必要がある場合clearTimeout、または必要な場合setInterval(そしておそらくclearInterval)、問題にさらにコードを投げる必要がありますが、実行する前に十分な時間が経過したことを確認するという原則がfunc保持されます。

于 2010-12-04T17:27:57.897 に答える
3

このアイテムは、私が取り組んでいるアプリケーションの大きな頭痛の種を解決するのに役立ちました。投稿してくれてありがとう。AdjustTickCount ユーティリティは、ソリューションを証明するための優れた不可欠なツールです。

この問題は IE 7 にも影響し、IE 6 にも影響しているように見えますが、ブラウザが一斉に応答を停止し、解決策がそのバージョンでも機能しないように見えるため、さらに悪い結果になります。特にビジネス/エンタープライズの世界では、これらの古いバージョンの多くのユーザーがまだいます。

左マウス ボタンが Windows XP の要因であることがわかりませんでした。それがなくても問題が発生します。

タイムアウトの遅延がせいぜい数秒で、アプリケーションに設定されているタイムアウトの数が非常に少ない場合は、最初の 2 つの回答で問題ありません。より長いタイムアウトが必要な場合は、Web アプリが使用できなくなるのを防ぐために、さらに多くの作業を行う必要があります。qooxdooなどのフレームワークを使用する Web 2.0 RIA では、ユーザーはフレームワークを何日も実行したままにすることができるため、アニメーションやその他の短い効果を生成するために、0.5 秒の数秒の遅延よりも多くの長いタイムアウトが必要になる場合があります。

最初の解決策は良いスタートですが、次のタイムアウトを に設定するtarget-nowと、再び関数がすぐに呼び出されます。これは、稼働時間 + 遅延が 2^32 ミリ秒を超えるため、稼働時間が 0 に戻るまで JS コードがスピンするためです (またはユーザーがブラウザーを強制終了します)。

2 番目の解決策は改善です。なぜなら、早​​すぎるタイムアウトは 1 秒間隔でのみ発生するためです。これまで、アップタイム ラップ アラウンドの 1 秒以内であり、他のコードがルックインを取得できるようになっていますが、実験では、ブラウザを作成するのに十分であることが示されています。保留中のタイムアウトが十分にある場合は使用できません。そして、アップタイムがラップアラウンドするまで引き続き続行されるため、要求された遅延が十分に長い場合、ユーザーはブラウザーを強制終了することを決定する可能性があります。

CPU をあまり消費しない解決策は、後続の各タイムアウトの遅延を前の遅延の半分の時間に設定することです。その遅延が 500 ミリ秒未満になるまで、アップタイムのラップアラウンド ポイントが差し迫っていることがわかります (< 1 秒先)。また、次のタイムアウトを に設定してtarget-now、さらに少数のサイクルの後に早すぎるタイムアウトのチェックを停止することができます。これに到達するまでにかかる時間は、元の遅延の長さと、呼び出されたときのアップタイム ラップ アラウンドにどれだけ近づいたかによって異なりますsetSafeTimeoutが、最終的には最小限の CPU 負荷で、ユーザーが長時間のスローダウンを経験することなく、アプリケーションは通常の動作に戻ります。

このようなもの:

function setSafeTimeout(func, delay) {
  var target = +new Date + delay;
  var newDelay = delay;
  var helper = function()
  {
    var now = +new Date;
    if (now < target)
    {
      newDelay /= 2; // halve the wait time and try again
      if(newDelay < 500) // uptime wrap around is imminent
      {
        newDelay = target-now; // go back to using original target
      }
      var handle = setTimeout(helper, newDelay);
      // if required record handle somewhere for clearTimeout
    }
    else
    {
      func();
    }
  };
  return setTimeout(helper, delay);
};

さらなる改良:

setTimeout()システムのアップタイム ティックが 2^32ms に近くない場合でも、予想よりも数ミリ秒早くコールバックを呼び出すことがあることがわかりました。この場合、上記の関数で使用される次の待機間隔は、元のターゲットまでの残り時間よりも長くなる可能性があり、その結果、当初の予定よりも長く待機することになります。

以下は、この問題を解決する別のバージョンです。

function setSafeTimeout(func, delay) {
  var target = +new Date + delay;
  var newDelay = delay;
  var helper = function()
  {
    var now = +new Date;
    if (now < target)
    {
      var timeToTarget = target-now;
      newDelay /= 2; // halve the wait time and try again
      if(newDelay < 500 || newDelay > timeToTarget) // uptime wrap around is imminent
      {
        newDelay = timeToTarget; // go back to using original target
      }
      var handle = setTimeout(helper, newDelay);
      // if required record handle somewhere for clearTimeout
    }
    else
    {
      func();
    }
  };
  return setTimeout(helper, delay);
};
于 2011-01-07T19:30:37.293 に答える
1

キャメロン・ジョーダンの答えのバリエーション:

function setSafeTimeout(func, delay) {
    var target = +new Date + delay;
    var helper = function() {
        var now = +new Date;
            if (now < target) {
                setTimeout(arguments.callee, 1000);
            } else {
                func();
            }
        }
    return setTimeout(helper, delay);
}

ヘルパー関数の目的は、IE8 がバグ状態にある場合に、1 秒に 1 回自分自身を呼び出すことです。

テストに役立つユーティリティは、AdjustTickCount です(ただし、Windows XP のみ)。たとえば、新しいティック カウントを 0xffff0000 に設定すると、ティック カウンタがロールオーバーする前に 65 秒間バグのある動作が発生します。たとえば、120 秒に設定されたタイマーは、正しく起動しません。

また、Windows XP では、バグのある setTimeout の動作は、この投稿のように、クリックされているマウスの左ボタンに関連付けられているように見えました。

于 2010-12-06T21:42:36.813 に答える