关于事件循环和异步(WEB前端底层核心-JS的运行机制)

一、浏览器的进程模型

1、何为进程?

        进程是计算机中运行的程序实例。它是操作系统进行资源分配和调度的基本单位。每个进程都有自己的地址空间、内存和执行状态。进程之间相互独立,彼此不能直接访问对方的资源。进程间的通信需要通过特定的机制,例如管道、套接字等,需要双方同意才可交互

2、何为线程

        线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个进程可以拥有多个线程,这些线程共享进程的资源,如内存空间、文件句柄等。

        与进程相比,线程更加轻量级,因为线程在同一进程下共享内存空间和其他资源,而进程之间的资源一般是独立的。由于线程之间的切换开销较小,所以线程可以更高效地实现并发操作。

线程通常包括以下几种类型:

  1. 用户线程:由应用程序开发者创建和管理的线程,运行在用户空间。

  2. 内核线程:由操作系统内核创建和管理的线程,运行在内核空间。内核线程可以访问操作系统的所有资源,并且具有更高的优先级。

        线程可以协同工作,共同完成复杂的任务,也可以提高程序的响应速度和并发处理能力。在多核处理器系统中,不同的线程可以被分配到不同的处理器核心上执行,从而实现并行处理。然而,线程之间的共享资源也可能导致竞争条件和死锁等问题,需要谨慎设计和管理。

3、浏览器的有哪些进程

a、主页进程类型

  1. 主进程(Browser Process):也称为浏览器引擎进程或浏览器内核进程,负责协调和控制整个浏览器的运行。它管理其他进程,处理用户界面的显示和用户交互,以及网络请求的处理。

  2. 渲染进程(Renderer Process):每个标签页(Tab)通常对应一个独立的渲染进程。渲染进程负责解析、布局和绘制网页内容,并执行相关的JavaScript代码。这种进程隔离的设计可以提高安全性和稳定性,因为即使一个标签页崩溃,其他标签页仍然可以正常运行核心重点)。

  3. 插件进程(Plugin Process):用于运行浏览器插件,如Flash播放器等。为了增强浏览器的安全性和稳定性,现代浏览器通常将插件进程单独隔离,以防止插件的错误导致整个浏览器崩溃。

  4. GPU进程(GPU Process):负责处理图形相关的任务,例如页面的绘制和动画效果。将图形任务交给独立的GPU进程可以提高性能和稳定性。

  5. 网络进程(Network Process):也称为网络代理进程,负责处理网络请求和数据传输。网络进程独立于渲染进程,可以提高安全性,并且可以并行处理多个网络请求,提高网页加载速度。

b、图解

打开浏览器任务管理器查看浏览器进程

设置及其他(下图的...) -> 更多工具->浏览器任务管理器

4、渲染进程有哪些线程、渲染主线程是如何工作的?

A、渲染进程中通常包含多个线程,每个线程负责不同的任务,常见的线程包括:

  1. 主线程(Main Thread):主线程负责处理用户交互、解析 HTML、构建 DOM 树、CSSOM 树、渲染树的构建和布局、页面绘制等操作。

  2. GUI 渲染线程(GUI Rendering Thread):负责将渲染树中的元素绘制到页面上,包括文字、颜色、图像等的渲染。

  3. 事件触发线程(Event Thread):负责管理用户输入事件(如鼠标点击、键盘输入等)的响应。

  4. 定时器触发线程(Timer Thread):负责管理定时器,定时触发执行定时任务。

  5. 网络请求线程(Network Thread):负责处理网络请求、数据传输等网络相关操作。

B、渲染主线程是如何工作的?

a、主要任务

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

  1. HTML 解析和构建 DOM 树:渲染主线程会解析 HTML 文档,并将其转换成 DOM(文档对象模型)树的结构,表示网页的结构和内容。

  2. CSS 解析和构建 CSSOM 树:渲染主线程会解析 CSS 样式文件,并将其转换成 CSSOM(CSS 对象模型)树的结构,表示网页的样式信息。

  3. 渲染树构建:渲染主线程会将 DOM 树和 CSSOM 树合并,形成渲染树(Render Tree),它描述了网页的视觉呈现结构。

  4. 布局(Layout):基于渲染树,渲染主线程会计算每个元素在屏幕上的准确位置和大小,以确定网页的布局。

  5. 绘制(Painting):渲染主线程会根据布局计算结果,将渲染树中的元素转换成绘制指令,然后交给浏览器的绘图引擎进行绘制,将内容显示在屏幕上。

  6. 响应用户交互:渲染主线程还负责处理用户的交互事件,例如鼠标点击、滚动等,根据用户的操作更新渲染树,并触发布局和绘制。

  7. JavaScript 执行:渲染主线程还负责执行 JavaScript 代码,包括处理页面上的脚本、响应事件、更新页面状态。

b、如何调度任务(事件循环)
核心: 将任务排队

第一步.:在最开始的时候,渲染主线程会进入无限循环。

第二步:每⼀次循环会检查消息队列中是否有任务存在。如果有,就取出第⼀个任务执行,执行完⼀个后进入下⼀次循环;如果没有,则进入休眠状态。

第三步: 其他所有线程(包括其他进程的线程)可以随时向消息队列添加任务。新任务会加到消息队列的末尾。在添加新任务时,如果主线程是休眠状态,则会将其唤醒以继续循环拿取任务。

这样任务就可以有序执行了、这个排队执行任务的过程就叫事件循环

二、关于异步

1、如何理解异步

        JavaScript 是一门单线程的语言,这意味着 JavaScript 代码在执行时只能使用一个主线程。在浏览器中,JavaScript 代码通常运行在浏览器渲染主线程中,这个主线程负责处理页面渲染、用户交互和 JavaScript 执行等任务。

        由于渲染主线程是单线程的,如果 JavaScript 代码采用同步的方式执行,即按照代码的顺序依次执行,会导致浏览器渲染主线程在执行 JavaScript 代码期间无法处理其他任务,从而造成页面卡顿、无法及时响应用户操作等问题。这种情况被称为“阻塞”,因为主线程被某个任务阻塞而无法继续执行其他任务(这是绝对不行的)。

        为了避免主线程阻塞,提高页面的响应速度和流畅性,JavaScript 在浏览器中通常采用异步执行的方式。通过事件循环机制和回调函数,JavaScript 可以将耗时的任务交给其他线程(如网络请求线程、定时器线程等)处理,并在任务完成后通过回调函数返回结果,从而避免主线程的阻塞。这样可以保证页面的渲染和用户交互不受影响,提高了用户体验。

未使用异步会形成"堵塞"

使用异步能解决"堵塞"

2、异步任务有哪些

异步任务包括以下几种常见类型:

  1. 定时器任务(Timer Tasks):使用 setTimeoutsetInterval 函数创建的定时器任务,可以在指定的时间间隔后执行回调函数。

  2. 事件监听任务(Event Listener Tasks):通过事件监听函数来处理用户交互、网络请求、文件加载等事件,当事件触发时执行相应的回调函数。

  3. 异步网络请求任务(Ajax/XHR Tasks):使用 XMLHttpRequest 或 Fetch API 发起的网络请求,在请求完成后执行相应的回调函数。

  4. Promise 回调任务(Promise Jobs):通过 Promise 对象的 thencatchfinally 方法注册的回调函数,在 Promise 状态改变时执行。

  5. 微任务(Microtasks):通过 Promisethencatchfinally 方法、MutationObserverqueueMicrotask 函数等注册的微任务,在主线程任务执行完毕、渲染之前执行。

3、异步任务执行的优先级

        过去把消息队列简单分为宏队列和微队列,这种说法目前已无法满足复杂的浏览器环境,取而代之的是⼀种更加灵活多变的处理方式。

        W3C最新解释网址:https://html.spec.whatwg.org/multipage/webappapis.html#p erform-a-microtask-checkpoint

        任务没有优先级,在消息队列中先进先出、但消息队列是有优先级的

        根据 W3C 的最新解释: 每个任务都有⼀个任务类型,同⼀个类型的任务必须在⼀个队列,不同类型的任务可以分属于不同的队列。 在⼀次事件循环中,浏览器可以根据实际情况从不同的队列中取出任务执行。 浏览器必须准备好⼀个微队列,微队列中的任务优先所有其他任务执行。

  1. 延时队列(Timer Queue):用于存放通过 setTimeoutsetInterval 创建的定时器到达后的回调任务。当定时器到达指定时间时,相应的回调任务将被添加到延时队列中等待执行。这些任务的优先级一般为「中」,即介于交互队列和微队列之间。

  2. 交互队列(Interaction Queue):用于存放用户操作后产生的事件处理任务,例如点击事件、键盘事件、鼠标事件等。这些任务对应的回调函数会被添加到交互队列中,其优先级通常为「高」,因为用户交互往往需要得到及时响应。

  3. 微队列(Micro Queue):用于存放需要尽快执行的任务,其优先级通常为「最高」。微队列中的任务包括 Promise 的回调函数、MutationObserver 的回调函数等,它们会在当前任务执行完毕后立即执行,以确保及时更新页面状态或响应异步操作的结果。

总结: 同步任务> 微队列>交互队列>延时队列

4、代码演示

console.log('1'); // 同步任务

setTimeout(function() {
  console.log('2'); // 延时队列
}, 2000);

Promise.resolve().then(function() {
  console.log('3'); // 微队列
  Promise.resolve().then(function() {
    console.log('4'); // 微队列
    Promise.resolve().then(function() {
      console.log('5'); // 微队列
      setTimeout(function() {
        console.log('6'); // 延时队列
      }, 2000);
});
Promise.resolve().then(function() {
  console.log('7'); // 微队列
});
setTimeout(function() {
  console.log('8'); // 延时队列
}, 0);

console.log('9'); // 同步任务

输出结果为

1
3
4
5
7
9
8
2
6

五、总结什么是事件循环

        事件循环⼜叫做消息循环,是浏览器渲染主线程的工作方式。 在 Chrome 的源码中,它开启⼀个不会结束的 for 循环,每次循环从消息 队列中取出第⼀个任务执行,而其他线程只需要在合适的时候将任务加入到队列末尾即可。 过去把消息队列简单分为宏队列和微队列,这种说法目前已无法满足复杂的浏览器环境,取而代之的是⼀种更加灵活多变的处理方式。 根据 W3C 官方的解释,每个任务有不同的类型,同类型的任务必须在同⼀ 个队列,不同的任务可以属于不同的队列。不同任务队列有不同的优先级, 在⼀次事件循环中,由浏览器自学决定取哪⼀个队列的任务。但浏览器必须 有⼀个微队列,微队列的任务⼀定具有最高的优先级,必须优先调度执行。

  • 30
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值