setTimeout/setInterval delay數值過大問題

delay 參數將轉換爲帶符號的32位整數,這有效地將延遲限制爲 2147483647 ms(約 24.8 天)

2147483647 === Math.pow(2, 31) - 1 === parseInt('01111111111111111111111111111111', 2)

在nodejs和瀏覽器中執行的情況有所差異

Nodejs 中

setInterval(callback, delay[, ...args])
setTimeout(callback, delay[, ...args])

When delay is larger than 2147483647 or less than 1, the delay will be set to 1. Non-integer delays are truncated to an integer.

行爲統一!當 delay 大於 2147483647 時,將會被設置爲 1。-- Here

// 下述 delay 都爲 1
setInterval(() => {
	console.log(+new Date())
}, 3333333000)

setInterval(() => {
	console.log(+new Date())
}, 9999999000)

setInterval(() => {
	console.log(+new Date())
}, 2147483647 + 1)

瀏覽器

  1. Let timeout be the second argument to the method, or zero if the argument was omitted.
  2. Apply the ToString() abstract operation to timeout, and let timeout be the result. [ECMA262]
  3. Apply the ToNumber() abstract operation to timeout, and let timeout be the result. [ECMA262]
  4. If timeout is an Infinity value, a Not-a-Number (NaN) value, or negative, let timeout be zero.
  5. Round timeout down to the nearest integer, and let timeout be the result.
  6. Return timeout.

關注第四點:如果超時是Infinity值,非數字(NaN)值或負值,則將超時設置爲零。Here

通過測試規律發現,瀏覽器中超過32位的,會自動截取32位,如果第32爲1,即負數,則將超設置爲0;否則會將後32位,轉化爲相應毫秒值進行執行!

parseInt('0000000000000000000101110111000', 2) === 3000

上述爲 3000 ms

示例1:將第32位變爲1

setTimeout(() => {
	console.log(+new Date())
}, parseInt('1000000000000000000101110111000', 2))  // 立即執行

示例1:將第32保持0,增加第33位爲1,讓數字溢出

setTimeout(() => {
	console.log(+new Date())
}, parseInt('10000000000000000000101110111000', 2))  // 3000ms後執行

其他: 現代瀏覽器中,setTimeout()/setInterval()

  • Timeouts throttled to ≥ 4ms
  • Timeouts in inactive tabs throttled to ≥ 1000ms

參考地址

  • https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout
  • https://nodejs.org/api/timers.html#timers_settimeout_callback_delay_args
  • https://www.w3.org/TR/2011/WD-html5-20110525/timers.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章