导航
HTML
CSS
JavaScript
浏览器 & 网络
版本管理
框架
构建工具
TypeScript
性能优化
软实力
算法
UI、组件库
Node
冷门技能
针对性攻坚(TODO)
基本认知

如上图,在任务管理器的进程一栏里,有道词典和有道云笔记就是进程,而在进程下又有着多个执行不同任务的线程。
进程
- CPU 正在进行的一个任务的运行过程的调度单位
- 浏览器是一个多进程的应用程序
- 进程是计算机调度的基本单位
- 进程包含线程,线程在进程中运行
- 没有进程就没有线程
- 为什么分进程:进程之间耦合低,chrome 一个 tab 就是一个单独进程,单个 tab 卡死不会影响其他 tab 页面的工作
- 任务管理器(mac 活动监视器)查看 chrome 的进程情况
- 浏览器有一个主进程(用户界面:指的是整个浏览器的界面,地址栏、标签栏、页面内容...)
- 每一个 tab 各自有独立的:
- 渲染进程(浏览器内核 Renderer,渲染引擎)
- 网络进程(网络请求)
- GPU进程(动画与 3D 绘制 e.g. css3 动画开启 GPU 加速就用的这个进程)
- 插件进程(vue devtool 等 chrome 插件)
- 每个进程里包含多个线程运行
渲染进程
- 包含
- GPU 渲染线程(渲染页面)
- JS 引擎线程
- 运行互斥
⇒ 渲染引擎线程
- 解析 HTML,CSS
- 构建 DOM、CSSOM / Render 树
- 初始布局与绘制
- 回流与重绘
⇒ JS 引擎线程
- 一个主线程与多个辅助线程
- 一个浏览器只有一个 JS 引擎(浏览器只有一个 JS 主线程,也就是所谓的 JS 是单线程的)
- 解析 JS 脚本
- 运行 JS 代码
为什么渲染线程与JS线程互斥
因为不能一边渲染内容,一边 JS 在修改 DOM。
所以
- JS 执行的时候,渲染挂起
- JS 引擎任务空闲,GPU 渲染更新
<div>Front of you</div>
<script>while(true) {}</script> <!-- JS一直在执行,不空闲 -->
<div>End of you</div> <!-- 页面一直卡死,不渲染任何东西 -->
<div>Front of you</div>
<script>
setTimeout(() => {
document.getElementByTagName('div')[0].innerText = 'Front of me';
}, 1000);
</script> <!-- JS执行完后,空闲 -->
<div>End of you</div> <!-- 页面渲染 Front of you 和 End of you,1s后改为 Front of me -->
事件
- 事件触发线程:事件环(Event Loop)线程(事件环是个单独线程管理的)
- 事件线程:用户交互事件(
onclick
)、定时器setTimeout
、Ajax
(这些都会单独开线程)
宏任务、微任务
- 创建进程目的
- 把任务区分为 A 和 B,使任务互相独立,互不干扰
- 创建线程目的
- 一个进程中还是有很多异步任务,这些任务又都属于这一个大任务。目的就是为了实现异步的执行条件(A、B 线程可以独立的做自己的任务)
- 宏任务
- 宿主提供的异步方法和任务
- script 的执行(JS 整体代码,比如
<script>...</script>
这段脚本整体,也是个宏任务)
- setTimeout、setImmediate、setInterval
- UI 渲染、UI 交互(Ajax、mouseover, click 各种事件回调)、I/O
- 微任务
- 语言标准(ECMA262)提供的API
- Promise、process.nextTick(NodeJS)、MutationObserver
- 优先级
- 微任务优先级高于宏任务
- 先清空当前任务队列中的所有微任务,再渲染,再去取宏任务中的一个执行
浏览器事件环的运行流程


- 先在 JS 引擎线程执行栈中执行同步代码(遇到宏任务,会执行它们,并把宏任务们的回调放进宏任务队列)
- 然后清空所有微任务
- GUI 渲染
- 取一个宏任务回调出来执行(先进先出)【其实处理的都是宏任务的回调函数 e.g. ajax 回调,setTimeout 回调,例如代码执行时,遇到 setTimeout ,其实是立马执行了 setTimeout,但把回调放进了宏任务队列】
- 再执行同步代码
- 清空微任务
- ... 循环
简单记顺序就是:
同步代码 → 清空所有微任务 → 渲染 → 取宏任务队列中一个出来执行