web-dev-qa-db-ja.com

大きなミリ秒の遅延値に対してsetTimeout()が「壊れる」のはなぜですか?

大きなミリ秒の値をsetTimeout()に渡すと、予期しない動作に遭遇しました。例えば、

setTimeout(some_callback, Number.MAX_VALUE);

そして

setTimeout(some_callback, Infinity);

両方ともsome_callbackを通過したかのように、すぐに実行されます0遅延として大きな数の代わりに。

なぜこれが起こるのですか?

91
Matt Ball

これは、setTimeoutが遅延を格納するために32ビットintを使用しているため、許可される最大値は

2147483647

試してみると

2147483648

問題が発生します。

これは、JSエンジンで何らかの形の内部例外を引き起こし、関数がまったく起動するのではなく、すぐに起動することを想定しているだけです。

127
OneSHOT

ここでいくつかの説明: http://closure-library.googlecode.com/svn/docs/closure_goog_timer_timer.js.source.html

タイムアウト値が大きすぎて符号付き32ビット整数に収まらない場合、FF、Safari、Chromeでオーバーフローが発生し、すぐにタイムアウトがスケジュールされる可能性があります。ブラウザが開いたままになるのに24.8日は妥当な予想を超えているため、これらのタイムアウトをスケジュールしない方が簡単です。

21
warpech

次を使用できます。

function runAtDate(date, func) {
    var now = (new Date()).getTime();
    var then = date.getTime();
    var diff = Math.max((then - now), 0);
    if (diff > 0x7FFFFFFF) //setTimeout limit is MAX_INT32=(2^31-1)
        setTimeout(function() {runAtDate(date, func);}, 0x7FFFFFFF);
    else
        setTimeout(func, diff);
}
17
Ronen

期限切れのセッションでユーザーを自動的にログアウトしようとしたときに、これにつまずきました。私の解決策は、1日後にタイムアウトをリセットし、clearTimeoutを使用する機能を維持することでした。

以下に小さなプロトタイプの例を示します。

Timer = function(execTime, callback) {
    if(!(execTime instanceof Date)) {
        execTime = new Date(execTime);
    }

    this.execTime = execTime;
    this.callback = callback;

    this.init();
};

Timer.prototype = {

    callback: null,
    execTime: null,

    _timeout : null,

    /**
     * Initialize and start timer
     */
    init : function() {
        this.checkTimer();
    },

    /**
     * Get the time of the callback execution should happen
     */
    getExecTime : function() {
        return this.execTime;
    },

    /**
     * Checks the current time with the execute time and executes callback accordingly
     */
    checkTimer : function() {
        clearTimeout(this._timeout);

        var now = new Date();
        var ms = this.getExecTime().getTime() - now.getTime();

        /**
         * Check if timer has expired
         */
        if(ms <= 0) {
            this.callback(this);

            return false;
        }

        /**
         * Check if ms is more than one day, then revered to one day
         */
        var max = (86400 * 1000);
        if(ms > max) {
            ms = max;
        }

        /**
         * Otherwise set timeout
         */
        this._timeout = setTimeout(function(self) {
            self.checkTimer();
        }, ms, this);
    },

    /**
     * Stops the timeout
     */
    stopTimer : function() {
        clearTimeout(this._timeout);
    }
};

使用法:

var timer = new Timer('2018-08-17 14:05:00', function() {
    document.location.reload();
});

そして、あなたはstopTimerメソッドでそれをクリアするかもしれません:

timer.stopTimer();
1
Tim

ここでタイマーに関するノードのドキュメントを確認してください: https://nodejs.org/api/timers.html (jsでも同様であると仮定します。ループベース

要するに:

遅延が2147483647より大きいか、1より小さい場合、遅延は1に設定されます

遅延は次のとおりです。

コールバックを呼び出す前に待機するミリ秒数

これらのルールに沿って、タイムアウト値が予期しない値にデフォルト設定されているようです。

1
SillyGilly

コメントはできませんが、すべての人に答えます。符号なしの値を取ります(明らかに負のミリ秒を待つことはできません)。したがって、最大値は「2147483647」なので、より高い値を入力すると0から開始されます。

基本的に遅延= {VALUE}%2147483647。

したがって、2147483648の遅延を使用すると、1ミリ秒になるため、インスタントプロシージャになります。

0
KYGAS