Recently I discovered a footgun in real life, which was related to setTimeout, I had to run a timeout for say 28 days for a sale timer, I had a UTC timestamp for the end day, so with the naive approach, I did this
const date1 = new Date(timestamp1); // Difference in milliseconds const timeout = date2.getTime() - Date.now(); setTimeout(()=>{ // some code to turn off some flags / remove some banner },timeout);
To my surprise, this did not work or worked too well, since the code within setTimeout executed without waiting for the timeout, I decided to debug in the browser, and I saw that the control jumps into the setTimeout callback almost instantly.
Looking at MDN page of setTimeout, https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value , it was clear that there is a max limit until which setTimeout() will run accurately, specifically
2,147,483,647ms or (24.8 days) or (2**31 - 1) ms, this is because browsers store the delay as a 32-bit signed integer internally.
So whenever you pass in a timeout of more than 24.8 days, there is an integer overflow and the code is executed immediately or rather with a lower timeout duration than expected. That's a bummer, and there is no error !!!
const days = 30; const timeout = days * 24 * 60 * 60 * 1000; console.log('timeto', timeout); setTimeout(function () { console.log('ticked immediately'); // --> executed almost instantly }, timeout); class LongTimeout { constructor(cb, timeout) { this.timeStart = document.timeline ? document.timeline.currentTime : performance.now(); this.lastAnimationFrame = this.runTimer(cb, timeout); } runTimer(cb, timeout) { if(this.cancelled) return; const currTimeStamp = performance.now(); const elapsed = currTimeStamp - this.timeStart; if (elapsed >= timeout) { cb(); window.cancelAnimationFrame(this.lastAnimationFrame); } else { console.log('tick', elapsed, timeout); this.lastAnimationFrame = requestAnimationFrame(() => this.runTimer(cb, timeout) ); } } cancelTimeout() { window.cancelAnimationFrame(this.lastAnimationFrame); this.cancelled = true; this.lastAnimationFrame = null; } } const longTimer = new LongTimeout(() => { console.log(`Tick after ${timeout}`); // timeout works -> does not execute immediately }, timeout);
The above is the detailed content of setTimeout - max timeout footgun. For more information, please follow other related articles on the PHP Chinese website!