Why does setTimeout() "break" for large millisecond delay values?
I came across some unexpected behavior when passing a large millis开发者_JAVA百科econd 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?
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.
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);
}
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.
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?
Can't comment but to answer all the people. It takes unsigned value ( you can't wait negative milliseconds obviously ) So since max value is "2147483647" when you enter a higher value it start going from 0.
Basically delay = {VALUE} % 2147483647.
So using delay of 2147483648 would make it 1 millisecond, therefore, instant proc.
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();
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.
精彩评论