我看过Jake Archibald talk about event loop,发现浏览器中有3个队列:
在执行其项目时的行为有所不同。
回叫队列一次执行一次回叫,这意味着执行回叫后,浏览器在继续下一个之前要检查是否还有其他事情(例如重新渲染页面)。
微任务队列执行其项目,直到队列为空(这可能导致无限阻塞)
动画队列用于requestAnimationFrame
回调,并且它会执行其项,直到队列为空,除了在当前执行的回调之一(这些回调在下一帧中执行)中添加的回调之外。
我想检查一下并创建了一个脚本,该脚本递归调用requestAnimationFrame
并排队了50个超时回调,这些回调将主线程阻塞了15ms。
for (let i = 0; i < 50; i++) {
setTimeout(() => {
block(15);
console.log(`timeout ${i}`)
})
}
const startDate = Date.now();
function raf() {
requestAnimationFrame((x) => {
if (Date.now() < startDate + 1000) {
console.log(`raf ${x}`)
raf();
}
})
}
raf();
function block(ms) {
const startDate = Date.now();
while (Date.now() < startDate + ms) { }
}
实时演示:https://stackblitz.com/edit/js-xdpbbb?file=index.js
由于块时间大致等于帧之间的时间(60fps给出16.666ms),所以我期望raf和超时回调之间存在交织的模式。但是结果却完全不同:
您可以看到raf回调之间大约有10个超时回调,大约需要150毫秒才能完成。为什么不这样: