在下面的文章use-case 1
中,他们在count
函数内更改setTimeout
调用的位置:
https://javascript.info/event-loop#use-案例1-拆分-cpu-hungry-任务
在第二种情况下,速度要快得多,他们用下面的句子来解释:
如果您运行它,很容易注意到它所花费的时间要少得多时间
为什么?
这很简单:正如你所记得的,浏览器中有最小的延迟4ms,用于许多嵌套的setTimeout调用。即使我们设置0,也是4ms(或更多(。因此,我们安排得越早,它运行得就越快。
对我来说,这是一个非常不清楚的解释,我根本不明白它们的意思。有人能更清楚、更详细地解释为什么这两起案件需要不同的时间吗?
setTimeout(fn, t)
将同步调度fn
向now + t
开火。
你引用的解释很到位:如果你在锁定计算机一段时间之前安排任务,它会在之前启动,而不是在之后启动。
// now = wall clock 0;
setTimeout(fn, 1000); // schedules `fn` to fire at wall clock 0 + 1000 = 1000
lockCPUFor(5000);
// now = wall clock 5000;
setTimeout(fn, 1000); // schedules `fn` to fire at wall clock 5000 + 1000 = 6000;
在任务结束时,我们现在处于=5000+几毫秒。第一个超时经过4s,所以我们将立即执行它。然而,第二次超时计划在大约一秒钟后启动,所以我们将等待大约一秒钟。
const origin = performance.now();
function fn(name) {
console.log(name, performance.now() - origin);
}
fn("begin"); // ~0.05
setTimeout(() => fn("setTimeout before"), 1000); // ~ 5010
lockCPUFor(5000);
setTimeout(() => fn("setTimeout after"), 1000); // ~6000
function lockCPUFor(t) {
const now = performance.now();
while (performance.now() - now < t) {}
}
但在我们的情况下,延迟是0而不是1000,所以这应该无关紧要。
正如您的报价所述,传递0
并不意味着您将真正拥有零超时。一些环境(例如Chrome&node.js(将始终具有1ms的最小超时(尽管Chrome正在积极尝试删除该最小超时(,Firefox也将添加一些最小超时;在页面加载时";,(实际上,他们有一个特殊的任务队列,用于在页面加载时超时,它的优先级比任何其他任务队列都低(
每个遵循HTML规范的UA在第五层嵌套时都会有4ms的超时:
let startTime;
let i = 0;
const fn = () => {
console.log("iteration #%s took %sms", i + 1, performance.now() - startTime);
if (++i < 6) {
startTime = performance.now();
setTimeout(fn, 0);
}
}
const startTest = () => {
startTime = performance.now();
setTimeout(fn, 0)
}
// avoid Firefox's weird at-load task queue
onload = startTest;
因此,即使您通过0
,UA自动添加的这几毫秒也会对回调的调度时间产生影响,在长任务之前进行调度确实可以避免大多数限制(如果长任务持续时间更长(
但请注意,如果您需要比setTimeout更快地挂接到事件循环,则有一些方法。