浏览器事件循环 event loop(消息循环)

发布时间 2023-10-18 15:37:22作者: 南巷清风--大R

 打开浏览器 即 开启一个浏览器进程 (主要负责浏览器UI,用户交互,子进程拉起关闭等)
  并由浏览器进程拉起网络进程(多Tab共享)采用多线程模式,GPU 进程(多Tab共享)等

  当每开启一个 tab 页,浏览器进程会负责为该 Tab 拉起一个渲染进程,每一个渲染进程都会拉起一个渲染主线程(单线程模式),以保证多 tab 之间的独    立性

1、渲染主线程的工作

  解析HTML 、 解析CSS、 计算样式、 布局、 处理图层、 调用GPU(画页面)、 执行 js 代码、 执行事件(消息)函数 、调取计时器 等等  

2、渲染主线程单线程会带来的问题及解决方案

  2.1 执行 js 代码过程中, 遇到异步事件(setTimeout,setInterval,Promise,fetch等) 或 渲染任务(改变dom) 或 用户交互 时,如何做到不阻碍js执行(页面卡死)

  解决这个问题的关键 其实就是 渲染主线程的 工作原理

  渲染主线程就是一个处于无限循环的事件(消息)处理器(Google 浏览器如此写到  for(;;) )

  执行 js ,渲染等的过程,就是在执行事件,当没有任务的时候,进入休眠,有新的任务时,唤醒

    那么渲染主线程的任务从哪里获得呢  答案是 消息(事件)队列 (message queue)

  W3C 规定这个东西是 event loop, Google源码 叫 message loop

     那么队列里的消息来自于哪里呢 

  答案是 来自其他所有线程,包含计时器线程、网络线程、交互线程等

    那么这些线程为什么会生成任务呢

  答案是 渲染主线程 在执行事件时 遇到了 异步事件(setTimeout,setInterval,Promise,MutationObserver,XHR,fetch,addEventListener等) 或 渲染任务(改变dom) 或 用户交互 时,分配相应任务给这些线程,而主线程继续向下执行

  总结就是 浏览器通过异步执行的方式,在遇到需要异步执行的任务时,主线程生成任务并交给相应线程去执行,而自己继续向下执行,当其他线程完成时,将回调函数包装成任务,加入到相应的队列中去,等待主线程的调度执行,从而保证了浏览器永不阻塞,即最大限度的保证了渲染主线程即使是单线程也能流畅运行

  2.2 当事件处理器处理完一个事件后,如何决定下一个谁被执行呢,按队列先进先出么,有没有紧急的事件要插队呢

  答案是 有的,W3C 只规定了浏览器必须有存在于 正常队列(宏队列)之外的 微队列,这个队列的执行顺序处于最高,只有该队列中没有了事件,才能去执行其它队列

  但是随着业务对发展,仅仅两个队列已经远远无法满足,于是衍生出了多种队列,但 要基于以下原则,同一种类型的事件只能在一个队列中,不同种类型的事件,可以分属于不同队列中,且微队列执行优先级最高

  其中主要的有 微队列 (执行优先级最高),延时队列:计时器线程计时结束后返回的事件(执行优先级中),交互队列:用户操作后产生的事件(执行优先级高),渲染队列

  2.3 关于以上原则,没有例外么

  答案是 当然有,而需要我们前端明确知道的暂时只有一个,那就是页面渲染的 reflow ,js 修改 dom  时不会立即生效,修改完后立即获取dom元素尺寸等事件时,会立即同步执行渲染,而不是等待当前事件执行结束