Skip to main content

One post tagged with "microtask"

View All Tags

· 3 min read
Swnb

看看下面的代码输出什么

example.ts

_10
requestAnimationFrame(() => {
_10
console.log(1)
_10
})
_10
_10
setTimeout(() => {
_10
console.log(2)
_10
})

如果你把上面的代码复制到浏览器的控制台, 输出结果应该是2 1 , 为什么?

按照常规的宏任务和渲染的理解, requestAnimationFrame 和 setTimeout 都是宏任务, 浏览器的执行顺序是 requestAnimationFrame -> 渲染(render) -> setTimeout, 输出的结果应该是 1 2 才对

这就要说到一个重点, 那就是宏任务和宏任务之间不一定会发生渲染, 浏览器会判断是否需要渲染, 即便是你在前一个宏任务里面更改了 dom 元素的属性, 也不一定会在下一个宏任务触发前发生渲染, 而 requestAnimationFrame 一定是伴随着渲染而触发的, 没有渲染就不会触发 requestAnimationFrame

浏览器真实的执行顺序是 宏任务 -> 微任务队列清空 -> 判断是否执行渲染 -> 需要渲染 ? requestAnimationFrame 宏任务触发 + 微任务队列清空 + 渲染

通过下面的例子,能更直观的理解这个过程


右边的示例, 每次点击 div 元素的时候, 都会调用 requestAnimationFramesetTimeout

多次点击 div 后, 你会发现大部分的输出的都是 2 1

加上一段代码, 每次点击都会更新 dom 的宽度, 你会发现输出大部分都是 1 2,只有少部分的 2 1 这是因为浏览器要发生频繁的渲染, 伴随着 requestAnimationFrame 的触发比 setTimeout 更加快, 但是浏览器仍然会跳过一些渲染

example.html

_19
<body>
_19
<div
_19
id="root"
_19
style="background-color: yellowgreen; width: 500px; height: 200px;"
_19
></div>
_19
<script>
_19
const div = document.querySelector("#root")
_19
_19
div.addEventListener("click", () => {
_19
requestAnimationFrame(() => {
_19
console.log(1)
_19
})
_19
_19
setTimeout(() => {
_19
console.log(2)
_19
})
_19
})
_19
</script>
_19
</body>