事件循环中的消息队列

一、理论体系背景

(一)问题域

事件循环(Event Loop)大家应该并不陌生,它是前端极其重要的基础知识。在平时的讨论或者面试中也是一个非常高频的话题。随着W3C规范的修改,各大浏览器也对事件循环进行相应修改。
根据 W3C 的最新解释:每个任务都有一个任务类型,同一个类型的任务必须在一个队列,不同类型的任务可以分属于不同的队列。在一次事件循环中,浏览器可以根据实际情况从不同的队列中取出任务执行。浏览器必须准备好一个微队列,微队列中的任务优先所有其他任务执行。

随着浏览器的复杂度急剧提升,W3C 不再使用宏队列的说法
下面涉及的代码示例,我将以谷歌浏览器为例进行演示。

(二)术语体系

1.进程和线程

  • 进程:工厂
  • 线程:工人
    进程:进程是操作系统分配的最小基本单位,进程中包含线程

必须是运行中的某个应用程序才能称得上是进程,也就是应用关闭,进程自然也寄了(进程是动态概念)

img

线程:线程由进程所管理。为了提升浏览器的稳定性和安全性,浏览器采取了多进程模型。

  1. 线程是进程的基本单位,一个进程由一个或多个线程组成,那么线程就是程序执行的最小单元(CPU调度的最小单位)
  2. 线程也是一个动态的概念,存在是暂时的而非永久的
  3. 进程与线程的区别:进程在运行的时候拥有独立的内存空间,也就是说每个进程占用的内存都是独立的
  4. 多个线程是共享内存空间的,但是每个线程的执行是相互独立的,线程必须依赖于进程才能执行,单独的线程无法执行,由进程控制线程的执行,没有进程就不存在线程

2.浏览器是多进程的

浏览器打开一个网页(Tab)相当于新起一个进程(每个进程内有自己的多线程

3.浏览器的进程有什么

img

  • 渲染进程

  • 负责将HTML,CSS,JS转化为可以用户可与之交互的网页,排版殷勤Blink和JavaScriptV8都运行在这个进程里,默认情况下,Chrome为每个Tab标签创建一个渲染进程。出于安全考虑,渲染进程跑在沙箱环境之下

  • 浏览器进程

    负责页面的展示以及交互

  • 网络进程(下载进程)

    负责页面网络资源的加载,之前作为一个模块运行在浏览器里,现在才成为一个单独的进程

  • GPU进程

    GPU的意思是:图形处理器,用于负责CSS 3D效果,网页,chorme UI的绘制

  • 插件进程

  • 负责插件的允许,插件容易崩溃,我们插件进程处理使得插件崩溃浏览器不崩溃

4.渲染主线程

(1)第一版线程模型

●如果有一些确定好的任务,可以使用一个单线程来按照顺序处理这些任务

image.png

第一版:线程的一次执行

(2)第二版线程模型

●要在线程执行过程中接收并处理新的任务,就需要引入循环语句和事件系统

image.png

第二版:在线程中引入事件循环

(3)第三版线程模型

●消息队列是一种数据结构,可以存放要执行的任务。它符合队列“先进先出”的特点,也就是说要添加任务的话,添加到队列的尾部;要取出任务的话,从队列头部去取。

image.png

●如果要接收其他线程发送过来的任务,就需要引入消息队列。

分为下面三个步骤:

  1. 添加一个消息队列;

  2. IO 线程中产生的新任务添加进消息队列尾部;

  3. 渲染主线程会循环地从消息队列头部中读取任务,执行任务。

image.png

第三版线程模型:队列 + 循环

渲染主线程是浏览器中最繁忙的线程,需要它处理的任务包括但不限于:

  • 解析 HTML
  • 解析 CSS
  • 计算样式
  • 布局
  • 处理图层
  • 每秒把页面画 60 次
  • 执行全局 JS 代码
  • 执行事件处理函数
  • 执行计时器的回调函数

思考题:为什么渲染进程不适用多个线程来处理这些事情?

要处理这么多的任务,主线程遇到了一个前所未有的难题:如何调度任务?

比如:

  • 我正在执行一个 JS 函数,执行到一半的时候用户点击了按钮,我该立即去执行点击事件的处理函数吗?
  • 我正在执行一个 JS 函数,执行到一半的时候某个计时器到达了时间,我该立即去执行它的回调吗?
  • 浏览器进程通知我“用户点击了按钮”,与此同时,某个计时器也到达了时间,我应该处理哪一个呢?

渲染主线程想出了一个绝妙的主意来处理这个问题:排队

image-20220809223027806

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

这样一来,就可以让每个任务有条不紊的、持续的进行下去了。

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

(4)谷歌消息队列

在这里插入图片描述

JS为何会阻碍渲染?

先看代码

<h1>hello world!!!</h1>
<button>change</button>
<script>
  const h1 = document.querySelector('h1');
  const btn = document.querySelector('button');

  // 死循环指定的时间
  function delay(duration) {
     
    const start = Date.now(
  • 16
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值