浅谈JavaScript的事件循环机制(Event Loop)- 浏览器篇|8月更文挑战

774 阅读3分钟

前言

我们先来看个实际中的场景:

在浏览器上打印出1~100万。

对于这个需求,我们第一个反应的代码是这样的:

  for (let i = 0; i < 100 * 10000; i++) {
    const div = document.createElement("div");
    div.innerText = i;
    document.body.append(div);
  }

看着好像没问题,当然这里有的同学会说,你这里操作了100万次dom,性能损耗很严重。这是一个问题,但是这个不是本文讨论的重点。

我们实际运行一下这段代码,发现一开始的时间(很短)会无法正常做交互,比如鼠标悬浮,选中文字等操作。那么为什么会造成这个现象呢?不急,我们一步步分析。

浏览器的渲染机制

以Chrome为例,一个标签页独占一个渲染进程,而JavaScript解释线程是属于这个渲染进程内的。

我们先聊下前置知识,JavaScript执行和屏幕渲染是互斥的,原因是屏幕渲染需要根据DOM结构,而JavaScript是有能力修改DOM的,所以会在JavaScript执行完毕后,执行渲染动作。一般来说,屏幕的渲染是60HZ,差不多16ms需要切换一张图片,人眼才不会意识到卡顿。

那么我们再回来看刚刚的代码

  for (let i = 0; i < 100 * 10000; i++) {
  }

结论很明显了,这句代码的执行明显超过了16ms!!!

事件循环机制

事件循环机制(Event Loop)就是为了解决这个问题而提出的。

简单来说,我们把大任务切成了多个小任务,让这些任务的执行和渲染流程交错进行。

计算100万个(1s) => 屏幕渲染

计算10万个(0.1s) => 屏幕渲染 => 计算10万个(0.1s) => 屏幕渲染 => 计算10万个(0.1s) => ...

这里就不贴出具体的代码实现了,感兴趣的同学可以自己动手写一下。

再探事件循环机制

事件循环机制(Event Loop)出现后,JavaScript的执行任务分为了两类:同步任务和异步任务。而异步任务,又分成了两类:

  • 宏任务(Macrotask)整体代码定时器I/O
  • 微任务(Microtask)PromiseMutationObserverobserver

这里我们需要注意一点,Promise本身是同步代码,但是它的回调then catch是异步

  new Promise((res, rej) => {
    res('ok') // 同步任务
  }).then((result) => {
    console.log(result) // 异步任务中的微任务
  })

其实我们会好奇,为什么是分成这两类?为什么不是三类或者一类?其实这是一种权衡的策略。宏任务的定义为耗时长的任务,微任务为耗时短的任务。在实际执行中,需要执行的任务分成多,所以就会有优先级的问题,其实宏任务和微任务的制定就是一种折中,为了权衡执行时间和运行效率。

优先级问题

我们刚刚提到了优先级的问题,那我们就来展开聊聊。一般情况下,微任务的执行优于宏任务。为什么是一般情况下呢?我们来看下这段代码:

  for(let i = 0; i < 10; i++) {
    setTimeout(() => {
      console.log('宏任务开始')
      for (let j = 0; j < 10; j++) {
        microtask();
      }
    })
  }

  function microtask() {
    return new Promise(async (res) => {
      console.log('微任务开始')
      await microtask();
      res();
    })
  }

注册10个宏任务 => 运行第1个宏任务 => 注册10个微任务 => 微任务执行过程中又注册了微任务 => 微任务执行过程中又注册了微任务 => ...

这是一个微任务的死循环,如果按微任务的执行优于宏任务的论据,宏任务是不可能触发第二个的,因为微任务队列一直在被push。

但是实际情况下,我们是可以看到第二个宏任务开始的。这是为什么呢?其实是Chrome V8做的一个小策略,在微任务过多的时候,会执行下一个宏任务。

小结

我们通过了开头的一个小例子,引入了事件循环机制(Event Loop),而执行任务分为了两类:同步任务和异步任务。而异步任务,又分成了两类:宏任务和微任务。一般情况下,微任务的执行优于宏任务。当在极端场景下,比如微任务实在是太多了,Chrome V8会先执行宏任务。

最后打波小广告,美团校招社招内推,不限部门,不限岗位,不限投递数量,海量hc,快来快来~