为什么在JavaScript环境下setInterval任务执行速度比setTimeout任务慢?

问题描述 投票:0回答:2

众所周知,

setInterval
setTimeout
任务是JavaScript事件循环中的宏任务; 然而,在运行一些测试后,我发现
setTimeout
似乎比
setInterval
具有更高的优先级,那么这是什么原因呢?

代码

  1. 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
*/

  1. 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 文档,但仍然无法深入了解它。

测试环境

  1. Google Chrome 版本 110.0.5481.177(正式版)(x86_64)
  2. Node.js v18.12.0

文档

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

javascript timer settimeout setinterval event-loop
2个回答
0
投票

我相信这取决于浏览器的实现,更正确的说法是

setInterval
可能并不慢,只是在优先级队列中比
setTimeout
低,或者可能是 Chrome 的实现将
setTimeout
放入事件循环比
setInterval
快。有趣的是,如果您在 Mozilla Firefox 中运行相同的代码,您会注意到首先执行的是间隔,其次是超时,就像您预期的那样(至少在我的机器上是这样)。你最好的选择是查看 V8 源代码,也许查看 Chromium 用于事件循环的 libevent 库,但我怀疑你是否需要深入挖掘。


0
投票

对于 Chrome,这是一个错误。这些任务将在同一个计时器任务队列中排队,并应按排队顺序触发。这里没有任何任务优先级的地方。

我确实打开了CRBUG#1421612.

如果你想要链接,

setTimeout
setInterval
调用相同的timer初始化步骤,在第13步要求超时后运行步骤,在timer任务上排队任务来源超时后运行步骤包括等待毫秒毫秒过去然后执行传递的completionSteps,但它有这个特定的步骤:

等待具有相同 globalorderingIdentifier 的算法的任何调用,在这个之前开始,并且其 milliseconds 等于或小于这个的,已经完成。

在我们的例子中,global显然是相同的,并且orderingIdentifier在这两种情况下都是字符串

"setTimeout/setInterval"
,而milliseconds
0

所以我们应该让我们的completionSteps按照我们排队的顺序触发。鉴于这些 completionSteps do queue a global task on the same task-source, our JS callbacks should be performed in the same order.

明显的罪魁祸首是一项积极的工作,旨在消除 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 规范,因此它们可以随心所欲地进行操作,与它进行比较以查看浏览器是否正常运行通常不是一个好主意。更喜欢使用其他供应商。

© www.soinside.com 2019 - 2024. All rights reserved.