Skip to content

内存模型

进程是程序运行时所需的内存空间,进程内的线程用于实现代码执行。浏览器是多进程的:

  • 浏览器进程
  • 网络进程
  • 渲染进程
  • ...

INFO

可以打开浏览器内置任务管理器查看浏览器进程。

默认情况下,浏览器为每个标签页开启一个渲染进程。 渲染进程是处于沙盒环境的,避免网络病毒对计算机硬件造成破坏。

渲染主线程

渲染进程内的主线程就是渲染主线程,它需要处理:

  • 解析 HTML
  • 执行脚本
  • 计算样式
  • 布局
  • 分层
  • 绘制

TIP

为什么渲染进程不开多个线程去执行这些任务?

事件循环

事件循环是为了解决任务调度问题,详见w3c 规范

  1. 主线程启动时进入死循环,每次循环检查消息队列中是否还有任务
  2. 如果有,则取出第一个任务执行;如果没有,则进入休眠状态,此时调用 requestIdleCallback
  3. 其他线程可以随时向消息队列的末尾添加任务,并唤醒主线程

异步

当主线程遇到一些需要等待的任务时,就把任务交给浏览器的其他线程去处理,然后去处理其他任务。 其他线程处理完后,把事先传递的回调包装成任务,并加入相应的消息队列中,等待主线程处理。 这保证了主线程永不阻塞。

js
/**
 * 主线程执行这段代码时,将计时器任务发送给计时线程,然后就去执行别的代码了
 * 计时线程收到任务后开始计时,1s 后将回调包装成任务,加入延时队列中
 */
setTimeout(() => console.log(0),1e3);
js
// 主线程执行这段代码后,会添加一个渲染任务到消息队列
document.querySelector('h1').textContent = 'new text';
// 主线程运行 3s,在此期间不会取出消息队列里的任务
const currentTimestamp = Date.now();
while (Date.now() - currentTimestamp < 3e3) {}

消息队列

一个事件循环可以有多个消息队列。

W3C 标准

  • 相同类型的任务必须放在相同消息队列
  • 不同类型的任务可以放在不同消息队列
  • 不同队列具有不同优先级,浏览器自行决定
  • 必须有微队列,并具有最高优先级

Chrome 实现

类型对应接口优先级
微队列PromiseMutationObserverqueueMicrotask最高
交互队列addEventListener
延时队列setTimeoutsetInterval
...