setTimeout(() => new Promise((r) => {console.log(1);r();}).then(() => console.log('1 mic')))
setTimeout(() => new Promise((r) => {console.log(2);r();}).then(() => console.log('2 mic')))
1
1 mic
2
2 mic
为什么两个setTimeouts
不在同一个事件循环中
我在谷歌后没有找到一个可以理解的答案
setTimeout(() => new Promise((r) => {console.log(1);r();}).then(() => console.log('1 mic')))
setTimeout(() => new Promise((r) => {console.log(2);r();}).then(() => console.log('2 mic')))
1
1 mic
2
2 mic
为什么两个setTimeouts
不在同一个事件循环中
我在谷歌后没有找到一个可以理解的答案
2条答案
按热度按时间myzjeezk1#
正如我在Community Wiki answer中所述,原因很简单,调用
setTimeout(fn)
将在超时到期后将一个新任务排队以执行回调。因此,每次调用
setTimeout
都将自己的任务排队。但是我必须注意,无论如何,您的测试不会告诉您是否在同一个事件循环迭代中运行了两个回调。
实际上,每次调用 * 回调时,都会在“运行脚本后清除”算法中执行一个微任务检查点。
因此,即使在同一个事件循环迭代中有多个回调被触发的情况下,我们也会在每个回调之间有一个微任务检查点。例如,
requestAnimationFrame()
回调都在同一个事件循环迭代中被触发,作为事件循环的“更新渲染”步骤的一部分。但即使在那里,微任务也会被触发:因此,使用微任务并不是一个很好的方法来检查是否在同一个事件循环迭代中触发了两个回调。要做到这一点,你可以尝试调度一个新的任务,就像我在那个例子中所做的那样。但即使这样也不是万无一失的,因为浏览器确实有相当复杂的任务优先级系统,我们不能确定它们何时会触发不同类型的任务。
支持浏览器的最佳方法是使用仍在开发中的Prioritized task
scheduler.postTask
方法。这允许我们发布具有最高优先级的任务,这样我们就可以检查两个回调是否确实在同一个事件循环迭代中。在不支持此API的浏览器中,我们不得不求助于使用MessageChannel
对象,这应该是发布高优先级任务的最接近的方法:i7uq4tfw2#
因为每个定时器回调都有自己的任务计划。