为什么 setTimeout()会“中断”大的毫秒延迟值?

当向 setTimeout()传递一个很大的毫秒值时,我遇到了一些意想不到的行为,

setTimeout(some_callback, Number.MAX_VALUE);

还有

setTimeout(some_callback, Infinity);

两者都会导致 some_callback几乎立即运行,就好像我传递的是 0而不是一个很大的数字作为延迟。

为什么会这样?

49645 次浏览

这是由于 setTimeout 使用32位 int 来存储延迟,因此允许的最大值为

2147483647

如果你愿意的话

2147483648

你的问题就出现了。

我只能假设这会在 JS 引擎中导致某种形式的内部异常,并导致函数立即激活,而不是完全不激活。

Number.MAX_VALUE

实际上不是一个整数。 setTimeout 允许的最大值可能是2 ^ 31或2 ^ 32

parseInt(Number.MAX_VALUE)

得到1而不是1.7976931348623157e + 308。

这里有一些解释: http://closure-library.googlecode.com/svn/docs/closure_goog_timer_timer.js.source.html

超时值太大,无法放入有符号的32位整数可能导致 在 FF、 Safari 和 Chrome 中溢出,导致超时 立即安排。简单地不安排这些更有意义 超时,因为24.8天超出了合理的预期 浏览器保持开启状态。

你可使用:

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

当我试图使用过期的会话自动注销用户时,无意中发现了这个问题。我的解决方案是在一天后重置超时,并保留使用 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();

在这里查看 Timers 上的节点文档: https://nodejs.org/api/timers.html(假设 js 之间也是一样的,因为它现在是基于事件循环的一个无处不在的术语

简而言之:

当延迟大于2147483647或小于1时,延迟将设置为1。

延迟是:

调用回调前等待的毫秒数。

似乎您的超时值被默认为这些规则中的意外值,可能吗?

无可奉告,只能回答所有人。 它采用无符号值(显然不能等待负毫秒) 因此,由于最大值是“2147483647”,当您输入一个较高的值时,它从0开始。

基本延迟 = { VALUE }% 2147483647。

因此,使用延迟2147483648将使其成为1毫秒,因此,即时过程。