浏览器事件循环 event loop(消息循环)
打开浏览器 即 开启一个浏览器进程 (主要负责浏览器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元素尺寸等事件时,会立即同步执行渲染,而不是等待当前事件执行结束
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库