众所周知,
setInterval
和setTimeout
任务是JavaScript事件循环中的宏任务;
然而,在运行一些测试后,我发现setTimeout
似乎比setInterval
具有更高的优先级,那么这是什么原因呢?
setTimeout
任务之前setInterval
任务setTimeout(() => {
console.log(`4`);
});
let id = setInterval(() => {
console.log(`5`);
clearInterval(id);
});
Promise.resolve().then(() => console.log(`2`));
queueMicrotask(() => {
console.log(`3`);
});
console.log(`1`);
/*
1
2
3
4
5
*/
setInterval
任务之前setTimeout
任务let id = setInterval(() => {
console.log(`5`);
clearInterval(id);
});
setTimeout(() => {
console.log(`4`);
});
Promise.resolve().then(() => console.log(`2`));
queueMicrotask(() => {
console.log(`3`);
});
console.log(`1`);
/*
1
2
3
4
5
*/
我已经阅读了一些 API 文档,但仍然无法深入了解它。
测试环境
https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide
https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html
对于 Chrome,这是一个错误。这些任务将在同一个计时器任务队列中排队,并应按排队顺序触发。这里没有任何任务优先级的地方。
我确实打开了CRBUG#1421612.
setTimeout
和setInterval
调用相同的timer初始化步骤,在第13步要求超时后运行步骤,在timer任务上排队任务来源。 超时后运行步骤包括等待毫秒毫秒过去然后执行传递的completionSteps,但它有这个特定的步骤:
等待具有相同 global 和 orderingIdentifier 的算法的任何调用,在这个之前开始,并且其 milliseconds 等于或小于这个的,已经完成。
在我们的例子中,global显然是相同的,并且orderingIdentifier在这两种情况下都是字符串
"setTimeout/setInterval"
,而milliseconds是0
。明显的罪魁祸首是一项积极的工作,旨在消除 Chrome 从一开始就对计时器设置的最小 1ms 毫秒 限制。似乎在删除该限制时,他们使
setTimeout()
和 setInterval()
使用不同的路径。
因此,这意味着使用 0
以外的任何其他值持续 milliseconds 将使其按预期运行:
const pre = document.querySelector(".actual pre");
const id1 = setInterval(() => {
pre.textContent += "setInterval()\n";
pre.style.color = "red";
clearInterval(id1);
}, 1); // non-zero timeout
const id2 = setTimeout(() => {
pre.textContent += "setTimeout()\n";
pre.style.color = "green";
}, 1); // non-zero timeout (same)
.expected pre { color: green };
<div class="expected">
<h4>Expected</h4>
<pre>setInterval()
setTimeout()
</pre>
</div>
<div class="actual">
<h4>Actual</h4>
<pre></pre>
</div>
请注意,Node 并没有完全遵循有关计时器和事件循环处理的 HTML 规范,因此它们可以随心所欲地进行操作,与它进行比较以查看浏览器是否正常运行通常不是一个好主意。更喜欢使用其他供应商。