1 min read
Event Loop

w3c: event loop chrome: message loop

浏览器主要进程

浏览器进程

界面显示、用户交互、子进程管理

网络进程

加载网络资源

渲染进程

渲染进程启动后,会启动一个渲染主线程,主线程负责执行 HTML、CSS、JS 代码 默认情况下,浏览器会为每个标签页开启一个渲染进程,以保证标签页之间互不影响

事件循环(message queue)

  1. 渲染主线程进入一个无限循环
  2. 每一次循环检查消息队列里是否有任务存在,如果有,取第一个执行,执行完成后进入下一次循环,如果没有,进入休眠
  3. 其他所有线程(包括其他进程的线程)可以随时向消息队列添加任务,添加到消息队列的队尾。如果添加时主线程是休眠状态,将其唤起继续循环

整个过程被称为事件循环(消息循环)

如何理解 JS 的异步?

JS 是一门单线程的语言,这是因为它运行在浏览器的渲染主线程中,而渲染主线程只有一个。

而渲染主线程承担着诸多的工作,渲染页面,执行 JS 都在其中运行。

如果使用同步的方式,就极有可能造成主线程产生阻塞,从而导致消息队列中的其他任务无法执行。一方面主线程会白白浪费时间等待,另一方面页面无法及时更新,给用户造成卡死现象。

所以浏览器采用异步的方式避免。具体做法是当某任务发生时,比如计时器、网络、事件监听,主线程将任务交给其他线程去处理,自身立即结束任务的执行,转而执行后续代码。其他线程完成时,将事先传递的回调函数包装成任务,加入消息队列的队尾,等待主线程调度执行。

在这种异步模式下,浏览器永不阻塞,从而最大限度的保证了单线程的流畅运行。

消息队列的优先级

任务没有优先级,在消息队列里先进先出。

W3C 的最新解释:

  • 每个任务都有一个任务类型,同一个任务的类型必须在同一个队列,不同类型的任务可以分属不同队列。在一次事件循环中,浏览器可以根据实际情况从不同的队列中取出任务执行。
  • 浏览器必须准备好一个微队列,并且优先级最高。

随着浏览器的复杂度急剧提升,W3C 不再使用 宏队列 的说法

任务类型源码位置:third_party/blink/public/platform/task_type.h

// TaskType 默认值
kDeprecatedNone = 0;
// 直接操作 DOM
kDOMManipulation = 1;
// 响应用户交互
kUserInteraction = 2;
// 对于由 setTimeout() 或 setInterval() 排队的任务。
// 根据嵌套层级,类型不同

一些任务类型的优先级

微队列:用于存放最快执行的任务【最高】(Promise)

交互队列:用于存放用户操作后产生的事件处理任务【高】

延时队列:用于存放计时器到达后的回调任务【中】