有很多因素会导致setTimeout的回调函数执行比设定的预期值更久,本节将讨论最常见的原因。
最小延时 >=4ms
在浏览器中,setTimeout()/setInterval() 的每调用一次定时器的最小间隔是4ms,这通常是由于函数嵌套导致(嵌套层级达到一定深度),或者是由于已经执行的setInterval的回调函数阻塞导致的。例如:
function cb() { f(); setTimeout(cb, 0); }
setTimeout(cb, 0);
setInterval(f, 0);
一直以来,不同浏览器中出现这种最小延迟的情况有所不同(例如Firefox) - 从其他地方调用了setInterval( ),或者在嵌套函数调用setTimeout( ) 时(嵌套级别达到特定深度时),都会出现超时延迟。
如果想在浏览器中实现0ms延时的定时器,你可以参考这里所说的{domxref("window.postMessage()")}}
Note: 最小延时, DOM_MIN_TIMEOUT_VALUE, 是4ms (但在Firefox中通常是是存储在 dom.min_timeout_value 这个变量中), DOM_CLAMP_TIMEOUT_NESTING_LEVEL 的第5层.
Note: 4 ms 是在 HTML5 spec 中精确的,并且在2010年及以后的跨浏览器中保持了一致,这个数值比 (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2)规定的嵌套函数的最小延时10ms更为精确。
超时延迟
除了"最小延时"之外,定时器仍然有可能因为当前页面(或者操作系统/浏览器本身)被其他任务占用导致延时。 需要被强调是, 直到调用 setTimeout()的主线程执行完其他任务之后,回调函数和代码段才能被执行。例如:
function foo() {
console.log('foo has been called');
}
setTimeout(foo, 0);
console.log('After setTimeout');
会在控制台输出:
After setTimeout
foo has been called
出现这个结果的原因是,尽管setTimeout 以0ms的延迟来调用函数,但这个任务已经被放入了队列中并且等待下一次执行;并不是立即执行;队列中的等待函数被调用之前,当前代码必须全部运行完毕,因此这里运行结果并非预想的那样。