🔥🔥🔥消息队列和事件循环,点进来你就明白了

325 阅读4分钟

浏览器架构

浏览器架构有浏览器主进程、网络进程、GPU进程、渲染进程、插件进程。

  1. 浏览器主进程:主要负责界⾯显示、⽤户交 互、⼦进程管理,同时提供存储等功能。

  2. 网络进程:主要负责⻚⾯的⽹络资源加载。

  3. GPU进程:⽹⻚、Chrome UI 界⾯ 都采⽤ GPU 绘制。

  4. 渲染进程:核⼼任务是将 HTML、CSS 和 JavaScript转换为⽹⻚,排版引擎 Blink 和 JavaScript 引擎 V8 运⾏在该进程中。

  5. 插件进程:主要负责插件的运⾏,因插件易 崩溃,需要通过插件进程来隔离,保证插件进程 崩溃不会对浏览器和⻚⾯造成影响。

image.png

渲染进程

渲染进程有主线程、I/O线程、合成线程、光栅化线程、预解析DOM线程、垃圾回收辅助线程等线程 image.png

事件循环

事件循环的简单流程:执行宏任务-->执行微任务-->执行延迟任务 message.png

事件循环的详细流程图(无优先级) image.png

任务类型

宏任务:消息队列中的任务

  1. 渲染事件(如解析 DOM、计算布局、绘制)
  2. 用户交互事件(如鼠标点击、滚动页面、放大缩小等)
  3. JavaScript 脚本执行事件
  4. 网络请求完成、文件读写完成事件 微任务:是一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前。
  5. MutationObserver
  6. Promise 延迟任务
  7. setTimeout、setInterval

代码执行解释


async function foo() {
    console.log('foo')
}
async function bar() {
    console.log('bar start')
    await foo()
    console.log('bar end')
}
console.log('script start')
setTimeout(function () {
    console.log('setTimeout')
}, 0)
bar();
new Promise(function (resolve) {
    console.log('promise executor')
    resolve();
}).then(function () {
    console.log('promise then')
})
console.log('script end')

代码打印顺序

  1. 打印 script start
  2. 打印 bar start
  3. 打印 foo
  4. 打印 promise executor
  5. 打印 script end
  6. 打印 bar end
  7. 打印 promise then
  8. 打印 setTimeout

Chrome的四次迭代

单消息队列会存在队头阻塞问题,导致用户交互任务等待执行的时间长,导致交互体验不好。

  1. 第一次迭代(action)

    增加多个不同优先级的消息队列,在交互阶段,下面几种任务都应该视为高优先级的任务:

    • 通过鼠标触发的点击任务、滚动页面任务;
    • 通过手势触发的页面缩放任务;
    • 通过 CSS、JavaScript 等操作触发的动画特效等任务。

image.png

结果(result): 无法保证任务相对执行顺序

如果将用户输入的消息或者合成消息添加进多个不同优先级的队列中,这种任务的相对执行顺序就会被打乱,有可能出现还未处理输入事件,就合成了该事件要显示的图片。

  1. 第二次迭代(action)

    根据消息类型来实现消息队列,采用静态优先级策略调度任务

    • 输入事件的消息队列,用来存放输入事件

    • 合成任务的消息队列,用来存放合成事件

    • 默认消息队列,用来保存如资源加载的事件和定时器回调等事件

    • 空闲消息队列,用来存放 V8 的垃圾自动垃圾回收等实时性不高的事件

image.png

结果(result): 使用静态优先级策略,网页的加载速度会被拖慢 14%

在交互阶段,采用上述这种静态优先级的策略没有什么太大问题的,在页面加载阶段,如果依然要优先执行用户输入事件和合成事件,那么页面的解析速度将会被拖慢。

  1. 第三次迭代(action)

    根据消息类型来实现消息队列,采用动态优先级策略调度任务。

image.png

在交互阶段,显示器从前缓冲区读取图片,和浏览器生成新的图像到后缓冲区的过程是不同步的, VSync 时钟周期和渲染引擎生成图片不同步会造成掉帧、卡顿、不连贯。

image.png

通过将执行用户交互的任务时,将合成任务的优先级调整到最高,合成线程已经工作,将下个合成任务优先级调整为最低,并将页面解析、定时器等任务优先级提升这个策略,绑定 VSync 时钟同步周期和浏览器生成页面周期。

image.png

结果(result): 解决了单消息队列会存在队头阻塞的问题,但还存在任务饿死问题。

任务饿死 在某个状态下,一直有新的高优先级的任务加入到队列中 就会导致其他低优先级的任务得不到执行。 image.png

  1. 第四次迭代(action)

    给每个队列设置了执行权重,连续执行了一定个数的高优先级的任务,中间会执行一次低优先级的任务。 image.png

总结

经过四次迭代,单消息队列从无优先级到有优先级。

事件循环的详细流程图(有优先级) image.png