事件循环
众所周知,事件循环是 JS 中的重点内容,通过学习该机制,不仅可以了解代码的执行顺序,还为后续编写高质量代码打下了基础,今天来看一道和浏览器事件模型结合起来的的题目
代码题
<div @click="fun">
<div @click="fun"/>
</div>
const fun = () => {
setTimeout(() => {
console.log(1)
Promise.resolve().then(() => {
console.log(2)
})
})
Promise.resolve().then(() => {
console.log(3)
setTimeout(() => {
console.log(4)
})
})
console.log('fun execute')
}
// fun execute
// 3
// fun execute
// 3
// 1
// 2
// 4
// 1
// 2
// 4
首先需要几个前置的知识点
-
事件循环机制为,先执行宏任务,再执行微任务,直到微任务不再产生,再开始执行下一个宏任务
-
dom 元素绑定的事件都是宏任务
-
setTimeout 是宏任务,promise 的回调是微任务
-
事件的冒泡/捕获比执行其回调的优先级要高
分析
第一次事件循环,点击 child 时,由于第四条规则和第二条规则,先添加了两个宏任务到宏任务队列中,如图
然后执行第一次 fun 函数,遇到定时器,加入宏任务队列,然后遇到 promise 回调,加入微任务队列,由于打印语句为同步代码,所以立即输出,至此,第一个 fun 函数执行完毕,此时任务队列状态如图
刚才执行 fun 时,产生了一个微任务,所以需要先清空微任务队列,所以接着打印了 3,遇到定时器,再添加一个宏任务进去,如图
至此,微任务队列被清空,第一次事件循环结束,开始执行第二个 fun 函数,遇到定时器,加入宏任务队列,遇到 promise 回调,加入微任务队列,然后执行打印语句,任务队列状态如下
然后清空微任务队列,打印 3,遇到了定时器,加入宏任务队列,如图
执行第一个定时器后,打印 1,然后产生了一个微任务,于是打印 2,此时微任务队列为空,所以执行下一个宏任务,打印4,如图
执行这个宏任务,和上面的流程一致,按顺序再次打印 1、2、4