CC的学习笔记(一)-- 事件循环

85 阅读4分钟

浏览器是多进程多线程的应用。

主要有浏览器进程、网络进程、渲染进程。

渲染进程

渲染进程启动后,会开启一个渲染主线程。默认情况下,浏览器会为每个标签页开启一个渲染主线程。

渲染主线程:主要处理解析 HTML,CSS,计算样式,布局,处理图层(z-index),按帧数每秒重绘页面(一般 60 帧,即重绘 60 次),执行全局 JS,执行各种回调等。

计算样式:

(1)确定声明值 (2)层叠 (3)继承 (4)使用默认值

确定声明值:如果某个属性在作者样式表或者浏览器样式表有明确的值,且没有冲突的情况,则直接取该值。

层叠 :上述有冲突的样式,则通过层叠计算,按权重取值。 作者样式表 > 浏览器默认样式表

继承:对于没有指定值的属性,判断其可以继承,则继承其父级元素的值。  (文本样式基本都是可以继承的)

默认值:上述三步走完仍然未确定的,则取该样式的默认值。

事件循环

如何理解 JS 的异步?JS 为什么需要异步?

JS 是单线程语言,原因在于它是运行在浏览器的渲染主线程,浏览器渲染主线程只有一个。 页面渲染、js 执行等诸多工作都是需要渲染主线程去承担的。如果 JS 没有异步的方式,使用同步的方式执行,那么极有可能导致渲染主线程阻塞,导致消息队列中很多后续任务无法被执行,这样一方面会导致主线程持续等待消耗时间,另一方面会导致页面渲染阻塞,页面无法及时更新,给用户造成卡死的假象。

所以浏览器采用异步的方式去避免这个问题。具体做法是当某些任务发生时,比如计时器、网络异步请求、事件监听等,主线程会把这种任务交给其他线程去处理,自己则立即结束该任务的执行,开始执行后续的任务。 当其他线程完成任务处理后,会将事先传递的回调函数包装成任务,加到主线程消息队里末尾,等待主线程调度执行。

任务是否有优先级?

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

但是消息队列有优先级

W3C 最新解释:

  • 每个任务都有一个任务类型,同一个类型的任务必须在一个队列,不同类型的任务可以分属不同的队列。在一次事件循环中,浏览器可以按实际情况去决定从不同队列取任务执行
  • 浏览器必须准备一个微队列,微队列中的任务优先所有其他任务的执行。

目前 chrome 至少包含了:

  • 延时队列:计时器回调,优先级 中
  • 交互队列:存放用户操作后产生的时间处理任务,优先级 高
  • 微队列:存放需要最快执行的任务,优先级 最高  ---------- 添加任务到微队列的主要方式是Promise、MutationObserver

常见面试题:

1、阐述一下 JS 的事件循环?

事件循环又叫消息循环,是浏览器渲染主线程的工作方式

在Chrome源码中,它开启一个不会结束的for循环,每次循环都会从消息队列中取出第一个任务执行,而其他线程只需要在合适的时机将任务加入到消息队列的末尾即可。

过去是把消息队列简单的分成了宏队列和微队列,这种说法已经无法满足复杂的浏览器环境了,取而代之的是一种新的处理方式。

按照新的解释,每个任务都有一个任务类型,同一类型的任务必须在同一队列,不同类型的任务也可以分属不同的队列。任务队列是有不同的优先级的,在一次事件循环中,浏览器自行决定取哪个队列的任务,但是必须有一个微队列,且微队列的优先级是最高的。,必须优先调度执行。

2、JS 中的计时器能做到准确精确计时么?为什么?

不能,因为:

1、计算机硬件没有原子钟,无法做到精确计时

2、操作系统的计时函数本来就有少量偏差 ,而 JS 的计时器最终调用的是操作系统的函数,也就会有偏差

3、按照W3C的标准,JS 计时器嵌套超过5层,默认就会有4ms的偏差

4、受事件循环的影响,计时器的回调函数只能再主线程空闲时运行,因此也会带来偏差。