结合事件模型,再看事件循环

141 阅读2分钟

事件循环

众所周知,事件循环是 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

首先需要几个前置的知识点

  1. 事件循环机制为,先执行宏任务,再执行微任务,直到微任务不再产生,再开始执行下一个宏任务

  2. dom 元素绑定的事件都是宏任务

  3. setTimeout 是宏任务,promise 的回调是微任务

  4. 事件的冒泡/捕获比执行其回调的优先级要高

分析

第一次事件循环,点击 child 时,由于第四条规则和第二条规则,先添加了两个宏任务到宏任务队列中,如图

image.png

然后执行第一次 fun 函数,遇到定时器,加入宏任务队列,然后遇到 promise 回调,加入微任务队列,由于打印语句为同步代码,所以立即输出,至此,第一个 fun 函数执行完毕,此时任务队列状态如图 image.png

刚才执行 fun 时,产生了一个微任务,所以需要先清空微任务队列,所以接着打印了 3,遇到定时器,再添加一个宏任务进去,如图

image.png

至此,微任务队列被清空,第一次事件循环结束,开始执行第二个 fun 函数,遇到定时器,加入宏任务队列,遇到 promise 回调,加入微任务队列,然后执行打印语句,任务队列状态如下

image.png

然后清空微任务队列,打印 3,遇到了定时器,加入宏任务队列,如图

image.png

执行第一个定时器后,打印 1,然后产生了一个微任务,于是打印 2,此时微任务队列为空,所以执行下一个宏任务,打印4,如图

image.png

执行这个宏任务,和上面的流程一致,按顺序再次打印 1、2、4