超时触发时间可能比预期时间长,原因有很多。本节介绍了最常见的原因。

嵌套超时

如 HTML 标准 中所述,浏览器会在setTimeout被嵌套调用5次后强制执行至少4毫秒的超时。

以下示例可以说明这一点,其中我们将对setTimeout的调用嵌套,延迟时间为0毫秒,并在每次处理程序被调用时记录延迟。前四次,延迟大约为0毫秒,之后大约为4毫秒。

html

Previous This Actual delay

jslet last = 0;

let iterations = 10;

function timeout() {

// log the time of this call

logline(new Date().getMilliseconds());

// if we are not finished, schedule the next call

if (iterations-- > 0) {

setTimeout(timeout, 0);

}

}

function run() {

// clear the log

const log = document.querySelector("#log");

while (log.lastElementChild) {

log.removeChild(log.lastElementChild);

}

// initialize iteration count and the starting timestamp

iterations = 10;

last = new Date().getMilliseconds();

// start timer

setTimeout(timeout, 0);

}

function logline(now) {

// log the last timestamp, the new timestamp, and the difference

const tableBody = document.getElementById("log");

const logRow = tableBody.insertRow();

logRow.insertCell().textContent = last;

logRow.insertCell().textContent = now;

logRow.insertCell().textContent = now - last;

last = now;

}

document.querySelector("#run").addEventListener("click", run);

* {

font-family: monospace;

}

th,

td {

padding: 0 10px 0 10px;

text-align: center;

border: 1px solid;

}

table {

border-collapse: collapse;

margin-top: 10px;

}

非活动选项卡中的超时

为了减少后台选项卡的负载(以及相关的电池消耗),浏览器会在非活动选项卡中强制执行最小的超时延迟。如果页面正在使用 Web Audio API AudioContext 播放声音,则也可能会取消此限制。

具体细节取决于浏览器。

Firefox 桌面版和 Chrome 都对非活动选项卡设置了至少1秒的超时。

Firefox for Android 对非活动选项卡设置了至少15分钟的超时,并且可能会完全卸载它们。

如果选项卡包含 AudioContext,则 Firefox 不会限制非活动选项卡。

跟踪脚本的限流

Firefox 对其识别为跟踪脚本的脚本实施了额外的限流。在前台运行时,限流的最小延迟仍然是4毫秒。但在后台选项卡中,限流的最小延迟为10,000毫秒,即10秒,并在文档首次加载30秒后生效。

有关更多详细信息,请参阅 跟踪保护。

延迟超时

如果页面(或操作系统/浏览器)忙于其他任务,超时也可能比预期晚触发。需要注意的一个重要情况是,在调用setTimeout()的线程终止之前,无法执行该函数或代码片段。例如

jsfunction foo() {

console.log("foo has been called");

}

setTimeout(foo, 0);

console.log("After setTimeout");

将写入控制台

After setTimeout

foo has been called

这是因为即使setTimeout被调用时延迟为零,它也会被放入队列中,并在下一个机会执行;而不是立即执行。当前正在执行的代码必须完成才能执行队列中的函数,因此最终的执行顺序可能与预期不符。

页面加载期间的超时延迟

Firefox 会在当前选项卡加载期间延迟触发setTimeout()计时器。触发将延迟到主线程被认为空闲(类似于 window.requestIdleCallback()),或直到加载事件被触发。