Why does setTimeout() “break” for large millisecon

2019-01-02 18:20发布

I came across some unexpected behavior when passing a large millisecond value to setTimeout(). For instance,

setTimeout(some_callback, Number.MAX_VALUE);

and

setTimeout(some_callback, Infinity);

both cause some_callback to be run almost immediately, as if I'd passed 0 instead of a large number as the delay.

Why does this happen?

6条回答
十年一品温如言
2楼-- · 2019-01-02 18:26

Some explanation here: http://closure-library.googlecode.com/svn/docs/closure_goog_timer_timer.js.source.html

Timeout values too big to fit into a signed 32-bit integer may cause overflow in FF, Safari, and Chrome, resulting in the timeout being scheduled immediately. It makes more sense simply not to schedule these timeouts, since 24.8 days is beyond a reasonable expectation for the browser to stay open.

查看更多
人间绝色
3楼-- · 2019-01-02 18:34

This is due to setTimeout using a 32 bit int to store the delay so the max value allowed would be

2147483647

if you try

2147483648

you get your problem occurring.

I can only presume this is causing some form of internal exception in the JS Engine and causing the function to fire immediately rather than not at all.

查看更多
步步皆殇っ
4楼-- · 2019-01-02 18:34

You can use:

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);
}
查看更多
低头抚发
5楼-- · 2019-01-02 18:39

I stumbled on this when I tried to automatically logout a user with an expired session. My solution was to just reset the timeout after one day, and keep the functionality to use clearTimeout.

Here is a little prototype example:

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);
    }
};

Usage:

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

And you may clear it with the stopTimer method:

timer.stopTimer();
查看更多
刘海飞了
6楼-- · 2019-01-02 18:40
Number.MAX_VALUE

is actually not an integer. The maximum allowable value for setTimeout is likely 2^31 or 2^32. Try

parseInt(Number.MAX_VALUE) 

and you get 1 back instead of 1.7976931348623157e+308.

查看更多
大哥的爱人
7楼-- · 2019-01-02 18:50

Check out the node doc on Timers here: https://nodejs.org/api/timers.html (assuming same across js as well since it's such an ubiquitous term now in event loop based

In short:

When delay is larger than 2147483647 or less than 1, the delay will be set to 1.

and delay is:

The number of milliseconds to wait before calling the callback.

Seems like your timeout value is being defaulted to an unexpected value along these rules, possibly?

查看更多
登录 后发表回答