阅读 28

了解浏览器中的Event Loop

1. 什么是Event Loop?它解决了什么问题?

  • Event Loop 是计算机系统的一种运行机制。
  • 浏览器和NodeJS基于不同的技术实现了各自的Event Loop。
  • EventLoop 解决了单线程模型对JavaScript构成的限制问题,因为所有任务都在一个线程上完成。一旦遇到大量任务或者遇到一个耗时的任务,网页就会出现"假死",因为JavaScript停不下来,也就无法响应用户的行为。

2. 浏览器中Event Loop 的执行机制是怎样的?什么是宏任务、微任务?

简单理解就是:

  • 在渲染进程中,有一个消息队列,它用来存放任务。
  • 有一个IO线程,它用来接收其他进程传进来的消息(比如网络进程的资源加载完成、浏览器进程的鼠标点击),接收到消息之后,会将这些消息组装成新任务,将新任务添加进消息队列尾部。
  • 还有一个主线程,它会循环地从消息队列头部中读取任务,并执行任务。

消息队列的任务类型有:如输入事件(鼠标滚动、点击、移动)、微任务、文件读写、WebSocket、JavaScript 定时器等等。除此之外,消息队列中还包含了很多与页面相关的事件,如 JavaScript 执行、解析 DOM、样式计算、布局计算、CSS 动画等。

举几个🌰:

  • 当接收到 HTML 文档数据,渲染引擎就会将“解析 DOM”事件添加到消息队列中
  • 当用户改变了 Web 页面的窗口大小,渲染引擎就会将“重新布局”的事件添加到消息队列中
  • 当触发了 JavaScript 引擎垃圾回收机制,渲染引擎会将“垃圾回收”任务添加到消息队列中
  • 要执行一段异步 JavaScript 代码,也是需要将执行任务添加到消息队列中

我们把消息队列中的任务称为宏任务,微任务和宏任务是绑定的,每个宏任务在执行时,会创建自己的微任务队列。假如在一个宏任务中,分别创建一个用于回调的宏任务和微任务,无论什么情况下,微任务都早于宏任务执行。 在执行JavaScript 脚本时,大体上的分类如下: 宏任务:整体代码script,setTimeout, setInterval 微任务:Promise.then(非new Promise)await后面的代码

小总结:执行宏任务,然后执行该宏任务产生的微任务,若微任务在执行过程中产生了新的微任务,则继续执行微任务,微任务执行完毕后,再回到宏任务中进行下一轮循环。

3. 说了这么多,通过下面的小题自己理解消化下

    async function async1() {
      console.log('A')
      await async2()
      console.log('B')
    }
    async function async2() {
      console.log('C')
    }

    console.log('D')

    setTimeout(function () {
      console.log('E')
    })

    async1()

    new Promise(function (resolve,reject) {
      console.log('F')
      setTimeout(function(){
      console.log(999)
      })
    }).then(function () {
      console.log('G')
    })

    console.log('H')
复制代码
  1. 整段代码作为宏任务,解析完后,开始执行,console.log('D'),输出D
  2. 遇到setTimeout,它的回调函数是宏任务,把它放入宏任务队列。
  3. 遇到async1(),执行 console.log('A'),输出A,执行await async2(),输出C。await 下一行后的代码,作为微任务,放到微任务队列。
  4. 遇到new Promise,输出F。setTimeout的回调作为宏任务,把它放入宏任务队列。then 里的放到微任务队列。
  5. 输出H
  6. 第一轮宏任务执行完了,然后检查微任务对象是否为空。
  7. 此时有2个微任务,挨个取出来执行。
  8. 输出B
  9. 因为没有resolve(),所以then里的不会输出G,输出undefine
  10. 微任务执行完了。开始执行下一轮宏任务。此时宏任务队列里有2个setTimeout的回调函数。输出E。然后检查微任务队列,无。再接着开始宏任务,输出999
  11. 所以输出结果为: D、A、C、F、H、B、undefinded、E、999。
console.log(1);

setTimeout(() => {
  console.log(2);
  Promise.resolve().then(() => {
    console.log(3)
  });
});

new Promise((resolve, reject) => {
  console.log(4)
  resolve(5)
}).then((data) => {
  console.log(data);
})

setTimeout(() => {
  console.log(6);
})

console.log(7);

复制代码
  1. 输出1
  2. setTimeout的回调函数作为宏任务,放入宏任务队列。
  3. new promise,输出4,then里的回调函数放入微任务队列。
  4. 又遇到setTimeout,回调函数作为宏任务,放入宏任务队列。
  5. 输出7
  6. 然后检查微任务队列,输出5
  7. 微任务队列执行完后,开始下一轮宏任务。
  8. 第一个setTimeout的回调函数,输出2。遇到promise.then,放入微任务队列。第二轮宏任务执行完了,检查它对应的微任务,输出3
  9. 微任务队列执行完了,开始下一轮宏任务,输出6
  10. 所以输出结果为:1、4、7、5、2、3、6
文章分类
前端
文章标签