浏览器事件循环

一、浏览器的进程模型

浏览器是一个多进程多线程的应用程序,浏览器内部工件极其复杂,为了减少连环崩溃的几率,当启动浏览器后,它会自动启动多个进程,其中,有以下主要进程:

1.浏览器进程

浏览器主要负责界面显示、用户交互、子进程管理等,浏览器进程内部会启动多个线程处理不同的任务。

2.网络进程

网络进程主要加载网络资源,也会启动多线程。

3.渲染进程

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

二、渲染主线程的工作

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

  • 解析HTML
  • 解析CSS
  • 计算样式
  • 进行布局
  • 处理图层
  • 连续渲染画面(FPS帧率,如每秒把页面画60次)
  • 执行全局JS代码
  • 执行事件处理函数
  • 执行计时器的回调函数等

渲染主线程的任务调度方式是排队,浏览器的任务会进入消息队列(事件队列)

三、事件队列(消息队列)

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

四、浏览器的异步机制

在代码执行过程中,会遇到一些无法立即处理的任务。例如::

  1. 计时完成后需要执行的任务——setTimeout、setInterval
  2. 网络通信完成后需要执行的任务——XHR、Fetch
  3. 用户操作后需要执行的任务——addEventListener

如果让渲染主线程等待这些任务的时机达到,就会导致主线程长期处于阻塞的状态,从而导致浏览器卡死。这是浏览器就会使用异步来解决这个问题。

具体操作

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

五、事件队列的优先级

任务没有优先级,但事件队列有优先级

在过去的浏览器中,有一个宏队列和一个微队列(VIP),而现在的浏览器中,每个任务都有一个任务类型,同一个类型的任务必须在一个队列中,不同类型的任务可以分属于不同的队列,在一次事件循环中,浏览器可以根据实际情况从不同的队列中取出任务执行,w3c规定,浏览器必须准备好一个微队列(micro task queue),微队列中的任务优先所有其他任务执行。

chrome中的队列优先级

在目前chrome中有延时队列(中)、交互队列(高)、微队列(最高)等,添加到微队列的主要方式是

Promise.MutationObserver

例如:

Promise.resolve().then(函数);     //立即把一个函数加入微队列

这里对上述两个方法进行一定的解释

Promise.MutationObserver

Promise.MutationObserver是一个基于Promise对象的封装实现,用于将MutationObserver和Promise结合起来处理DOM树的变化。

MutationObserver是浏览器提供的一个API,用于监视DOM树的变化。它可以观察DOM节点的增删改操作,并在变化发生时触发回调函数。然而,原生的MutationObserver API并不支持使用Promise来处理异步操作。

为了解决这个问题,Promise.MutationObserver通过封装MutationObserver对象并返回一个Promise对象来处理异步操作。它可以将MutationObserver的回调函数包装成Promise的resolve或reject函数,从而能够使用Promise的链式调用和错误处理机制。

使用Promise.MutationObserver可以实现以下功能:

  1. 监视DOM树的变化,并在变化发生后执行一系列的异步操作。
  2. 可以通过链式调用的方式依次执行一系列的异步操作,并在最后返回一个Promise对象。
  3. 可以通过catch方法捕获错误,并进行相应的错误处理。

以一个例子来说明Promise.MutationObserver的使用:

const observer = new Promise.MutationObserver(callback);observer.observe(document.body, {subtree: true,childList: true,
});observer.then(() => {console.log('DOM树发生了变化');
})
.catch((error) => {console.error(error);
});function callback(mutations, observer) {// 异步操作setTimeout(() => {console.log('异步操作完成');observer.takeRecords(); // 清空记录observer.disconnect(); // 停止观察observer.resolve(); // 触发Promise的resolve函数}, 1000);
}

在上面的例子中,我们创建了一个Promise.MutationObserver对象,并传入一个回调函数。然后通过调用observe方法开始观察DOM树的变化。

当DOM树发生变化时,回调函数会执行一系列的异步操作,最后调用resolve函数来触发Promise对象的resolve。我们可以通过then方法来监听resolve的触发,并执行相应的操作。

如果在异步操作过程中发生了错误,可以通过catch方法来捕获错误并进行处理。

总之,Promise.MutationObserver是一个方便而强大的工具,可以简化DOM树变化的处理,并且提供了Promise的链式调用和错误处理机制。

Promise.resolve().then()

Promise.resolve().then()是Promise对象的一个方法链,用于处理异步操作的结果。

Promise.resolve()是一个静态方法,它返回一个已经被解决(resolved)的Promise对象。它可以接受一个值作为参数,并将这个值封装成一个已经解决的Promise对象返回。如果参数本身就是一个Promise对象,则直接返回该Promise对象。

.then()方法是Promise对象的方法,用于添加回调函数来处理Promise对象的解决结果。它接受两个参数:一个是解决的回调函数,一个是拒绝的回调函数。在Promise对象解决后,会调用对应的回调函数来处理解决的值。

结合起来,Promise.resolve().then()的作用可以总结为:

  1. 创建一个已经解决的Promise对象;
  2. 添加回调函数来处理解决的结果。

通过这个方法链,我们可以进行一系列的异步操作,并在每个异步操作完成后,使用.then()方法链式地处理结果。这样的代码结构更加简洁,易于理解和维护。

下面是一个简单的例子来说明Promise.resolve().then()的使用:

Promise.resolve(42).then((value) => {console.log(value); // 42return value + 1;}).then((value) => {console.log(value); // 43return Promise.reject('出错了');}).catch((error) => {console.error(error); // 出错了return Promise.resolve('处理错误');}).then((value) => {console.log(value); // 处理错误});

在上面的例子中,我们首先使用Promise.resolve()创建了一个已经解决的Promise对象,并传入值42。

然后通过.then()方法添加了第一个回调函数,打印出了解决的值42,并返回了value + 1。

接下来,在第二个.then()方法里,我们返回了一个被拒绝的Promise对象,并在.catch()方法里捕获了错误,并打印出了错误信息"出错了"。

接着,我们又通过.then()方法添加了一个回调函数,并返回了一个已经解决的Promise对象,并且打印出了返回的值"处理错误"。

通过这个例子,我们可以看到,使用Promise.resolve().then()方法可以方便地处理多个异步操作的结果,并且可以在每个.then()方法里进行相应的处理和错误处理。

总之,Promise.resolve().then()是Promise对象的方法链,用于处理异步操作的结果,它简化了异步操作的处理和错误处理。

六、JS的相关案例及知识

1.JS阻塞渲染

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>js阻碍渲染案例</title>
</head>
<body><button>点击我启动函数</button><div><h1>启动函数前</h1></div>
</body>
<script>let btn = document.querySelector("button");let h1 = document.querySelector("h1");btn.addEventListener('click',function(){h1.textContent = "我是函数启动后";function abc(){let start = Date.now();while(Date.now()-start<3000);};abc();});
</script>
</html>

在上述代码中,页面会卡住3秒后再进行重新渲染,死循环函数会占用渲染主线程3秒,这段时间内,如h1的更改操作,滚动页面等都会进入等待,在事件队列中排队。

2.JS中的计时器不能精准计时的原因

  • 计算机硬件误差(计算机内没有原子钟)
  • 该方法调用的是操作系统的计时函数,但操作系统的计时函数本身误差
  • w3c中规定,如果嵌套层超过5层,则会带有4毫秒的最少时间
  • 受事件循环的影响,计时器的回调函数只能在主线程空闲时运行

七、总结

单线程是异步产生的原因,事件循环是异步的实现方式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/589380.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023-12-14 LeetCode每日一题(用邮票贴满网格图)

2023-12-14每日一题 一、题目编号 2132. 用邮票贴满网格图二、题目链接 点击跳转到题目位置 三、题目描述 给你一个 m x n 的二进制矩阵 grid &#xff0c;每个格子要么为 0 &#xff08;空&#xff09;要么为 1 &#xff08;被占据&#xff09;。 给你邮票的尺寸为 stam…

git常用命令详解

git常用命令详解 Git 是一个分布式版本控制系统&#xff0c;用于追踪文件的变化并协作开发。以下是一些常用的 Git 命令及其详细说明&#xff1a; 初始化仓库&#xff1a; git init说明&#xff1a; 在当前目录下初始化一个新的 Git 仓库。 克隆仓库&#xff1a; git clone &…

快速上手:探索Spring MVC的学习秘籍!

SpringMVC概述 1&#xff0c;SpringMVC入门案例1.2 案例制作步骤1:创建Maven项目步骤2:补全目录结构步骤3:导入jar包步骤4:创建配置类步骤5:创建Controller类步骤6:使用配置类替换web.xml步骤7:配置Tomcat环境步骤8:启动运行项目步骤9:浏览器访问步骤10:修改Controller返回值解…

单片机数据发送程序

#include<reg51.h> //包含单片机寄存器的头文件 /***************************************************** 函数功能&#xff1a;向PC发送一个字节数据 ***************************************************/ void Send(unsigned char dat) { SBUFdat; whil…

【新手向】VulnHub靶场MONEYBOX:1 | 详细解析

MONEYBOX:1 安装靶机 作为一名新手&#xff0c;首先要配置好环境&#xff0c;才能进行下一步的操作。 将下载的ova文件导入VirtualBox。 VirtualBox下载地址&#xff1a;https://www.oracle.com/cn/virtualization/technologies/vm/downloads/virtualbox-downloads.html 选择…

手把手教你如何配置 AWS WAF 入门

文章目录 1 前言2 新手第一步3 实践3.1 了解托管规则3.2 编写自己的DIY规则3.3 配置实战A&#xff0c;控制泛洪攻击&#xff08;攻击请求速率&#xff09;3.4 配置实战B&#xff1a;当检查到特定路径请求的时候拒绝对方的试探 4 更进一步4.1 什么是合理的规则设计&#xff1f;如…

机器学习 | Python实现基于GRNN神经网络模型

文章目录 基本介绍模型设计参考资料基本介绍 GRNN 是 Nadaraya-Watson 估计器神经网络的改进,其中向量自变量上的标量的一般回归被计算为以核作为加权函数的局部加权平均值。 该算法的主要优点是其校准只需要为核估计定义适当的带宽。 因此,GRNN 比其他前馈人工神经网络算法更…

Leetcode每日一题:1599.经营摩天轮的最大利润

前言&#xff1a;本题是一道逻辑细节题&#xff0c;考察阅读理解并转化为代码的能力&#xff0c;很多细节 题目描述&#xff1a; 你正在经营一座摩天轮&#xff0c;该摩天轮共有 4 个座舱 &#xff0c;每个座舱 最多可以容纳 4 位游客 。你可以 逆时针 轮转座舱&#xff0c;但…

基于图神经网络的动态物化视图管理

本期 Paper Reading 主要介绍了发布于 2023 年 ICDE 的论文《Dynamic Materialized View Management using Graph Neural Network》&#xff0c;该文研究了动态物化视图管理问题&#xff0c;提出了一个基于 GNN 的模型。在真实的数据集上的实验结果表明&#xff0c;取得了更高的…

redis 从0到1完整学习 (十二):RedisObject 之 List 类型

文章目录 1. 引言2. redis 源码下载3. redisObject 管理 List 类型的数据结构3.1 redisObject 管理 List 类型3.2 List PUSH 源码 4. 参考 1. 引言 前情提要&#xff1a; 《redis 从0到1完整学习 &#xff08;一&#xff09;&#xff1a;安装&初识 redis》 《redis 从0到1…

Vue Router的介绍与引入

在这里是记录我引入Vue Router的全过程&#xff0c;引入方面也最好先看官方文档 一.介绍 Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成&#xff0c;让用 Vue.js 构建单页应用变得轻而易举。功能包括&#xff1a; 嵌套路由映射动态路由选择模块化、基于组件的…

鸿蒙OS应用开发之气泡提示

前面学习了弹窗提示,其实有时候只是想在旁边做一些说明,那么采用弹窗的方式就比较麻烦一些,这时可以采用系统里面的气泡提示方式。 系统也提供了几种方式弹出气泡提示,最简单的一种是采用bindPopup属性。它的定义如下: 在后面的参数设置里,也是比较复杂的形式。我们先来演…

MyBatis-Plus 基础:LambdaQueryWrapper详解与实例

LambdaQueryWrapper 是 MyBatis-Plus&#xff08;一个 MyBatis 的增强工具&#xff09;中用于构造 SQL 查询条件的一个非常强大的工具。它允许你以 Lambda 表达式的方式构建查询条件&#xff0c;从而避免了硬编码字段名&#xff0c;提供了类型安全&#xff0c;并且使得代码更加…

内存泄漏检测工具

1. vs/vc(windows下)自带的检测工具 将下面的语句加到需要调试的代码中 #define _CRTDBG_MAP_ALLOC // 像一个开关,去开启一些功能,这个必须放在最上面 #include <stdlib.h> #include <crtdbg.h>// 接管new操作符 原理: 就是使用新定义的DBG_NEW去替换代码中的n…

Jetpack Compose中使用Android View

使用AndroidView创建日历 Composable fun AndroidViewPage() {AndroidView(factory {CalendarView(it)},modifier Modifier.fillMaxWidth(),update {it.setOnDateChangeListener { view, year, month, day ->Toast.makeText(view.context, "${year}年${month 1}月$…

第13课 一维数组

文章目录 前言一、数组的概念二、一维数组的定义三、一维数组的初始化四、一维数组的使用及举例1. 元素顺次前移的问题2. 数组元素逆序调整问题3. 统计输入的各个数据的个数 五、课后练习1. 从数组中查找某个元素2. 求一个数组中元素的平均值和均方差3. 编程统计某班某次考试的…

python 1200例——【15】杨辉三角

杨辉三角(Pascal’s Triangle)是一个在数学上非常有名的三角阵列,每个数是它上面两数之和。代码如下: # 获取用户输入的杨辉三角的行数 n = int(input("输入需要打印的杨辉三角行数 :")) # 确保输入的是正整数 assert n > 0, "请输入正整数!"# 初…

x-cmd pkg | openssl - 密码学开源工具集

目录 简介首次用户技术特点竞品分析进一步阅读 简介 OpenSSL 是一个开源的密码库和 SSL/TLS 协议实现&#xff0c;它提供了一组密码学工具和加密功能&#xff0c;用于保护数据通信的安全性。项目发展历史可以追溯到 1998 年&#xff0c;源自 Eric A. Young 和 Tim J. Hudson 开…

Docker的一个简单例子(一)

文章目录 环境示例准备构建启动/停止容器更新应用分享应用 参考 环境 RHEL 9.3Docker Community 24.0.7 示例 准备 从github克隆 getting-started-app 项目&#xff1a; git clone https://github.com/docker/getting-started-app.git查看项目&#xff1a; ➜ getting-s…