Skip to content

Latest commit

 

History

History
74 lines (44 loc) · 3.92 KB

browser-04.md

File metadata and controls

74 lines (44 loc) · 3.92 KB

【浏览器原理-04】 页面循环系统

#develop/Front-End/浏览器原理

1、消息队列和事件循环

每个渲染进程都有一个主线程,但主线程非常繁忙,要同时处理DOM、计算样式、处理布局、JS任务以及各种输入事件,所以需要引入一个系统来统筹调度这些任务,这个系统就是接下来要讨论的消息队列和事件循环系统

我们先从最简单的入手

1.1 单线程:顺序处理任务

  • 任务1:1+1
  • 任务2:2+2
  • 任务3:3+3
  • 。。。一系列同步任务
  • 任务结束

1.2 在线程运行过程中处理新任务

上面的任务都是事先安排好的,但是如果在线程运行中,需要接收并执行新的任务,那么就需要引入事件循环机制了

  • 引入循环机制,在线程后添加一个for循环语句,让线程一直循环执行
  • 引入事件,把一个任务理解为一个事件,一次for循环可以等待一个事件的执行

1.3 处理其他线程发送过来的任务

如果另外一个线程想要让主线程执行一个任务,以上的模型是做不到的,比如下图

那么我们需要引入消息队列的概念

消息队列是一种数据结构,其实就是栈,先进先出

然后改造后的第三版模型

1.4 跨进程执行任务

有了第三版的消息队列模型,实现了主进程下线程之间的消息通信,那再扩大一点,跨进程之间的任务处理也是类似的,在渲染进程中有一个专门的IO线程用来接收其他进程传来的消息,然后组装成任务推进消息队列

1.5 单线程的缺点

  • 如何处理高优先级的任务
  • 如何解决单个任务执行时间过长的问题

第一个问题,比如监控DOM节点变化,执行业务逻辑 通用方案是JS设计监听接口,渲染引擎同步调用接口,但带来的问题是每次变化时都执行JS接口,当前任务会被无限拉长,执行效率过低;但如果把DOM变化做成异步消息事件,推到消息队列尾部,那么监控的实时性又会降低

如果 DOM 发生变化,采用同步通知的方式,会影响当前任务的执行效率;如果采用异步方式,又会影响到监控的实时性。

这时候微任务就诞生了,把DOM变化的任务推进微任务队列,等当前宏任务执行完后,不是立马去执行下一个宏任务,而是先把当前宏任务下的微任务队列给执行清空,这样就比较合理的兼顾了实时性和效率的问题

但是,如果这个宏任务中存在一个耗时比较长的任务呢,也就是第二个问题,单任务执行过长阻塞,如图所示

比如用JS执行动画,如果某个任务执行时间过长,占用了动画单帧的时间,页面就会卡顿,也就是掉帧,在JS中我们通过滞后该任务,也就是回调的形式,后面再讨论

1.6 小结

  • 简单同步线程模型,执行事先确认好的同步任务
  • 引入循环语句和事件系统,在线程执行中接收并处理新的任务
  • 引入消息队列,接收其他线程发送过来的任务
  • 由渲染进程的IO线程来接收其他进程通过IPC发送过来的任务,继续沿用消息队列模型
  • 为了优化消息队列的效率和实时性问题,引入了微任务