将大毫秒值传递给
setTimeout()
时,我遇到了一些意外的行为。例如,
setTimeout(some_callback, Number.MAX_VALUE);
和
setTimeout(some_callback, Infinity);
两者都会导致
some_callback
几乎立即运行,就好像我通过了 0
而不是一个很大的数字作为延迟。
为什么会出现这种情况?
这是由于 setTimeout 使用 32 位 int 来存储延迟,因此允许的最大值为
2147483647
如果你尝试
2147483648
你的问题就出现了。
我只能推测这会在 JS 引擎中导致某种形式的内部异常,并导致该函数立即触发而不是根本不触发。
您可以使用:
const MAX_INT32 = 2 ** 31 - 1 // 2147483647 (hex 0x7FFFFFFF)
function runAtDate(date, func) {
const now = Date.now();
const then = date.valueOf();
const diff = Math.max(then - now, 0);
if (diff > MAX_INT32) {
setTimeout(() => {
runAtDate(date, func, MAX_INT32);
}, MAX_INT32);
} else {
setTimeout(func, diff);
}
}
或者,稍微复杂一点的版本,允许您使用返回值清除超时:
const MAX_INT32 = 2 ** 31 - 1 // 2147483647 (hex 0x7FFFFFFF)
function runAtDate(date, func, msPerTimeout = MAX_INT32) {
const timeout = arguments[3] ?? { valueOf() { return this._val; } };
const now = Date.now();
const then = date.valueOf();
const diff = Math.max(then - now, 0);
if (diff > msPerTimeout) {
timeout._val = setTimeout(() => {
runAtDate(date, func, msPerTimeout, timeout);
}, msPerTimeout);
} else {
timeout._val = setTimeout(func, diff);
}
return timeout
}
// Usage (using a much lower `msPerTimeout` value for demo purposes)
void (async () => {
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
// v1 (running to completion)
{
const timeout = runAtDate(
Date.now() + 1000,
() => console.log('v1 done!'),
100,
);
console.log('v1 @ 0ms:', timeout.valueOf());
await delay(500);
console.log('v1 @ 500ms:', timeout.valueOf());
await delay(500);
console.log('v1 @ 1000ms:', timeout.valueOf());
}
console.log('---')
// v2 (clearing the timeout)
{
const timeout = runAtDate(
Date.now() + 1000,
() => console.log('v2 done! (you’ll never see this message)'),
100,
);
console.log('v2 @ 0ms:', timeout.valueOf());
await delay(500);
console.log('v2 @ 500ms:', timeout.valueOf());
clearTimeout(timeout);
await delay(500);
console.log('v2 @ 1000ms:', timeout.valueOf());
}
})();
这里有一些解释:http://closure-library.googlecode.com/svn/docs/closure_goog_timer_timer.js.source.html
超时值太大而无法放入带符号的 32 位整数可能会导致 FF、Safari、Chrome 中溢出,导致超时 立即安排。 不安排这些更有意义 超时,因为 24.8 天超出了合理的预期 浏览器保持打开状态。
在此处查看有关计时器的节点文档:https://nodejs.org/api/timers.html(假设在 js 中也相同,因为它现在是基于事件循环的普遍存在的术语
简而言之:
当延迟大于2147483647或小于1时,延迟将设置为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();
无法发表评论,只能回答所有人。 它需要无符号值(显然你不能等待负毫秒) 因此,由于最大值为“2147483647”,当您输入更高的值时,它会从 0 开始。
基本上延迟 = {VALUE} % 2147483647。
因此使用 2147483648 的延迟将使其变为 1 毫秒,因此,即时过程。
Number.MAX_VALUE
实际上不是一个整数。 setTimeout 的最大允许值可能是 2^31 或 2^32。 尝试一下
parseInt(Number.MAX_VALUE)
您将得到 1,而不是 1.7976931348623157e+308。