浏览器的进程模型
什么是进程
程序在运行时需要一定的内存空间,我们可以简单的把这块内存空间理解为进程。
不同程序之间的进程是相互独立的。
什么是线程
在进程中来执行程序的就是线程。
一个进程至少需要有一个线程,所以在程序启动时会自动创建一个线程来运行代码, 我们称之为 主线程。
如果程序需要同时执行多段代码,主线程 就会启动更多的线程来执行,因此就会存在多个线程。
浏览器有哪些进程及线程
由于浏览器的工作原理非常复杂,因此浏览器是一个 多进程多线程 的应用程序。
浏览器主要有三个进程:
- 浏览器进程:主要负责界面(非页面)显示、用户交互、子进程管理等。浏览器进程内部会启动多个线程处理不同的任务。
- 网络进程:负责加载网络资源。网络进程内部会启动多个线程来处理不同的网络任务。
- 渲染进程:负责执行 HTML、CSS、JS 代码。渲染进程启动后,只会开启一个线程,这个线程就是渲染主线程。
浏览器进程跟网络进程都是多线程的,而渲染进程只会开启一个线程,这是因为浏览器无法做到在渲染进程中开启多个线程。
渲染主线程的工作原理
渲染主线程是浏览器中最繁忙的线程,需要它处理的任务包括但不限于:
- 解析 HTML
- 解析 CSS
- 计算样式
- 布局
- 处理图层
- 每秒把页面画 60 次
- 执行全局 JS 代码
- 执行事件处理函数
- 执行计时器的回调函数
- ......
这么多的任务需要处理,如何去调度成了一个关键问题。
渲染主线程采取的思路如下:
- 在最开始的时候,渲染主线程会进入一个无限循环。
- 每一次循环会检查消息队列中是否有任务存在。如果有,就取出第一个任务执行,执行完一个后进入下一次循环;如果没有,则进入休眠状态。
- 其他所有线程(包括其他进程的线程)可以随时向消息队列添加任务。新任务会加到消息队列的末尾。在添加新任务时,如果主线程是休眠状态,则会将其唤醒以继续循环拿取任务。
这个过程,就被称之为事件循环(消息循环)
什么是异步
代码在执行过程中,有一些任务是无法立即执行的,例如:
-
定时器相关的任务:
setTimeout、setInterval -
网络请求相关的任务:
XHR、Fetch -
用户交互相关的任务:
addEventListener
在事件循环过程中,如果遇到这些任务时浏览器采取同步的方式,即等待前一个任务结束再去执行下一个任务,那么渲染主线程就会发生阻塞。
所以浏览器选择采取 异步 的方式来执行任务,根据任务的类型来将任务放到不同的消息队列中。
任务的优先级
每个任务有不同的类型,同类型的任务必须在同一个队列,不同的任务可以属于不同的队列。
任务没有优先级,在消息队列中先进先出,但消息队列是有优先级的。
在目前的浏览器中,主要包含了下面的队列:
- 延时队列:用于存放计时器到达后的回调任务,优先级「中」
- 交互队列:用于存放用户操作后产生的事件处理任务,优先级「高」
- 微队列:用户存放需要最快执行的任务,优先级「最高」
可以通过 Promise.resolve().then(函数) 添加任务到微队列。