在?聊聊浏览器事件循环机制

news2024/11/25 15:27:38

目录

前言 

同步/异步编程模型

同步

异步

JS异步模型

调用栈

任务队列

宏任务队列

微任务队列

微任务API

事件循环

队列优先级

混合队列

事件循环实现

总结

参考文章

Event-Loop可视化工具


前言 

JS是单线程语言,在某个时间段只能执行一段代码。这种单线程模型的好处是不会出现多线程的竞态条件和死锁等问题:在多线程中,某个资源同时被其他线程调度时可能会出现执行顺序不确定导致错误,或者资源占用等待这一类的问题。因此JS无法同时处理多任务,为了处理这类任务,JavaScript运行时使用了一种叫事件循环机制的异步编程模型

JS的事件循环机制是一种异步编程模型,其特点是异步非堵塞,它决定了JavaScript的异步执行顺序和运行机制。

JS的事件循环机制由调用栈(Call Stack)、任务队列(Task Queue)、事件循环(Event Loop)组成,它们共同协作实现了JavaScript的异步编程模型,下面我会具体介绍这三个部分以及相关知识

同步/异步编程模型

首先我们熟悉一下同步和异步编程的概念

同步

同步编程模型是一种线性的编程模型,程序必须按照代码的顺序一个接一个地执行任务,直到当前任务执行完毕后,才能执行下一个任务

许多语言有sleep()函数,用于延迟或定时操作,sleep会阻塞当前线程或进程,直到暂停时间结束后才会继续执行下一条语句,这就是同步编程的特点

同步编程模型的优点是简单、直观,易于调试,缺点是程序执行效率比较低,容易阻塞程序的运行

异步

异步编程模型是一种非线性的编程模型,异步模型中任务的执行是非阻塞的,程序不必等待当前任务完成,而是可以继续执行下一个任务,同时等待当前任务的结果

在JS中我们可以同时执行多个异步函数,通过回调,事件,Promise等方式来捕获异步结果,此外,许多语言还引入了async/await的概念用于异步操作

异步编程模型的优点是可以提高程序的执行效率和吞吐量,缺点是比同步复杂,需要处理回调、事件、异常等问题。

JS异步模型

上面我们说到调用栈、任务队列、事件循环共同组成了JS的异步模型,那么我们就来看看其三者之间的关系及工作原理

调用栈

调用栈的作用是存储函数调用的上下文信息,其信息包括函数的参数、局部变量、返回地址等;调用栈是先进后出的结构,每当程序执行一个函数时,该函数会被压入调用栈的顶部,执行结束后会从调用栈中弹出

我们使用可视化工具模拟调用栈的操作

function a() {
    console.log('a');
}
function b() {
    console.log('b');
    a();
}
function c() {
    console.log('c');
    b();
}
c();

 

可以看到,函数在执行时会先压入栈,等完全执行完毕后就会被销毁

任务队列

队列的数据结构与上面的栈不同,采用的是先进先出(FIFO)的数据结构,JS的任务队列包含宏任务队列与微任务队列。之前写过一篇关于任务队列的文章,介绍了一下队列的特点:先进入的数据会被优先处理,后进入的数据则会被推迟处理,直到前面的数据处理完毕

JS有两种任务队列,分别是宏任务队列(Macro Task Queue)和微任务队列(Micro Task Queue)

宏任务队列

宏任务队列包括了所有的异步任务,如setTimeout、setInterval、requestAnimationFrame、UI交互事件等。

我们可以在可视化工具中运行以下下面的代码

setTimeout(function a() { }, 100);
setTimeout(function b() { }, 50);
setTimeout(function c() { }, 0);
function d() { }
setTimeout(function e() { }, 0);
d();

效果如下

可以看到使用setTimeout的函数会按顺序放在TaskQueue中,等主线程的所有任务都执行完成后才会从TaskQueue中取出延时函数或者轮询函数放在调用栈中运行

微任务队列

微任务队列包括了Promise回调函数、MutationObserver等。

我们也使用可视化工具对下面的代码进行模拟操作

Promise.resolve()
    .then(function d() { });
Promise.resolve()
    .then(function c() { });
Promise.reject()
    .catch(function b() { });
function a() { }
a()

Promise的回调函数和宏任务队列类似,使用其定义的函数会先放到微任务队列中,等待主线程任务全部执行完成后,再按顺序将函数压入调用栈进行函数执行

微任务API

此外浏览器还内置了微任务队列的API(queueMicrotask),用于手动创建微任务,用法如下

queueMicrotask(function b() {
  console.log("B");
});
Promise.resolve().then(function a() {
  console.log("A");
});

我们同样将其代入到工具中,效果如下:

使用queueMicrotask创建的微任务与promise回调相同,定义时会放在微任务队列中

事件循环

说了这么多终于来到了本文的核心部分:事件循环

JS的事件循环是用于协调调用栈和任务队列的机制,它的运行机制是不断监听调用栈和任务队列的状态,根据一定的规则来决定下一步要执行哪个任务。当调用栈为空时,事件循环会从任务队列中取出一个任务,并将其压入调用栈中执行。当调用栈不为空时,事件循环会等待调用栈中的任务执行完毕,再去检查任务队列是否有任务等待执行,周而复始,形成一个事件帧

队列优先级

结合上面说到事件循环的运行流程,在监听任务队列时会根据某种规则来决定运行方式,此种规则就与宏任务与微任务的优先级有关

看看下面的代码

setTimeout(function a() {});
Promise.resolve().then(function b() {});

我们同样借助可视化工具看看效果

可以看到微任务的优先级是高于宏任务的,在主线程执行完任务后首先执行的是微任务,等微任务队列的任务数为0时再执行宏任务

混合队列

了解了上述的理论后,深入进阶的问题就变得迎刃而解了,我们将宏任务,微任务和主线程的队列混合做个示例看看事件循环的运行流程

思考以下代码的执行顺序

setTimeout(function a() {
  console.log("A");
  Promise.resolve().then(function d() {
    console.log("D");
  });
}, 0);

Promise.resolve().then(function b() {
  console.log("B");
  setTimeout(function e() {
    console.log("E");
  }, 0);
});
fetch("https://baidu.com").then(function f() {
  console.log("F");
});
function c() {
  console.log("C");
}
queueMicrotask(function g() {
  console.log("G");
});
c();

结果如下

执行顺序是C->B->G->A->D->E->F,是不是和你的答案一样呢?

事件循环实现

经过上述例子的介绍,相信大家对JS的异步模型也有了一定的认识,那么我们使用之前讲到的迭代器实现一下事件循环

class EventLoop {
  microTaskQueue = []; // 微任务队列
  macroTaskQueue = []; // 宏任务队列
  static loop = function* (el) {
    // 使用迭代器实现事件循环,每次调用next都是一个新的循环
    while (true) {
      el.runMicroTasks();
      el.runMacroTasks();
      yield;
    }
  };
  queueMicroTask = (task) => {
    // 新增微任务
    this.microTaskQueue.push(task);
  };
  queueMacroTask = (task) => {
    // 新增宏任务
    this.macroTaskQueue.push(task);
  };
  runMicroTasks = () => {
    // 运行微任务
    while (this.microTaskQueue.length > 0) {
      this.microTaskQueue.shift()();
    }
  };
  runMacroTasks = () => {
    // 运行宏任务
    while (this.macroTaskQueue.length > 0) {
      this.macroTaskQueue.shift()();
    }
  };
}

最后我们运行一下上面的事件循环

const el = new EventLoop();// 创建事件循环
const { queueMacroTask, queueMicroTask } = el;
const { loop } = EventLoop;
const iterator = loop(el);// 创建循环迭代
iterator.next();// 第一次循环
queueMacroTask(function () {// 增加宏任务
  console.log("B");
});
queueMicroTask(function () {// 增加微任务
  console.log("A");
});
iterator.next();
queueMicroTask(function () {
  console.log("E");
});
queueMacroTask(function () {
  console.log("C");
});
queueMicroTask(function () {
  console.log("D");
});
iterator.next();
iterator.next();
// A B E D C

至此,我们就实现了一个简单的事件循环机制

总结

本篇文章介绍了同步/异步编程模型,同步堵塞线程,但是逻辑简单,异步非堵塞运行,但是需要对结果做处理;JS的异步模型由调用栈、任务队列、事件循环共同组成,其中事件循环充当了组织者,当主线程的任务都执行完成,则执行微任务队列中的任务,等任务执行完成后再运行宏任务;最后我使用JS实现了一个简单的事件循环,帮助理解。

以上就是文章全部内容了,希望能对你有帮助,如果觉得文章不错的话,还望三连支持一下,感谢!

参考文章

✨♻️ JavaScript Visualized: Event Loop - DEV Community

Event-Loop可视化工具

JS Visualizer 9000

http://latentflip.com/loupe/

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

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

相关文章

tailwindcss 多上下文与独立分包

tailwindcss 多上下文与独立分包 你看过动漫《百兽王》吗?《百兽王》的主人公是五个飞行员,他们分别驾驶黑、红、青、黄、绿五头机器狮,它们平时可以单独进行作战,遇到强敌时,也能进行五狮合体,成为巨大机…

论文写作全攻略

【基于Citespace和vosviewer文献计量学相关论文 】 文献计量学是指用数学和统计学的方法,定量地分析一切知识载体的交叉科学。它是集数学、统计学、文献学为一体,注重量化的综合性知识体系。特别是,信息可视化技术手段和方法的运用&#xff0…

uniapp写出文本域,右下角并限制字数200

<view class"textarea_box"><textarea class"textarea" placeholder"请填写10字以上的问题描述&#xff0c;以便我们更好的帮助您解决问题&#xff0c;提高产品质量。" placeholder-style"font-size:28rpx" maxlength"2…

图片怎么压缩到200K以内?来试试这几种压缩方法

怎么把图片压缩到200K以内呢&#xff1f;在日常生活中&#xff0c;不管是工作还是出门游玩&#xff0c;都会使用图片&#xff0c;当这些图片的内存太大时&#xff0c;保存和发送会成为一种难题&#xff0c;有的网站甚至无法上传超过一定内存的照片&#xff0c;那么我们怎么给照…

组态王与FX5u之间EtherNet/IP无线以太网通信

在实际系统中&#xff0c;同一个车间里分布多台PLC&#xff0c;通过上位机集中控制。通常所有设备距离在几十米到上百米不等。在有通讯需求的时候&#xff0c;如果布线的话&#xff0c;工程量较大耽误工期&#xff0c;这种情况下比较适合采用无线通信方式。 本方案以组态王和2…

磨刀不误砍柴工,五款让你事半功倍的软件

有句老话这样讲&#xff0c;工欲善其事&#xff0c;必先利其器&#xff0c;好的工具可以让你工作起来事半功倍。 网页收藏夹——Pocket Pocket是一款用于保存和阅读网页的工具。它可以让你把你感兴趣的网页保存到你的账户中&#xff0c;并提供多种功能和选项来优化你的阅读体…

PHP初中英语在线考试系统的设计与实现-计算机毕设 附源码87564

PHP初中英语在线考试系统的设计与实现 摘 要 本文研究的初中英语在线考试系统主要功能模块包括&#xff1a;学生用户管理、考试信息、成绩分析、通知公告管理&#xff0c;采取面对对象的开发模式进行软件的开发和硬体的架设&#xff0c;能很好的满足实际使用的需求&#xff0c;…

【深度学习】4-2 误差反向传播法 - 简单层的实现(层的介绍)

下面把构建神经网络的“层”实现为一个类。这里所说的“层”是神经网络中功能的单位。 下面先从一些简单的层开始介绍 乘法层的实现 层的实现中有两个共通的方法(接口)forward()和backward()。 forward() 对应正向传播 backward() 对应反向传播 现在来实现乘法层。看下面代…

SpringBoot 如何使用 Servlet 容器

SpringBoot 如何使用 Servlet 容器 SpringBoot 是一个非常流行的 Java 开发框架&#xff0c;它提供了一个简单而强大的方式来创建基于 Servlet 容器的 Web 应用程序。本文将介绍 SpringBoot 中如何使用 Servlet 容器。 Servlet 容器简介 Servlet 容器是指能够运行 Servlet 和…

一文带你认识FPGA LCMXO2-7000HC-4FG484C 带你深入了解其原理及特点

莱迪思深力科 LCMXO2-7000HC-4FG484C MachXO2系列 可编程逻辑器件 (PLD) 由六个超低功耗、即时启动、非易失性 PLD 组成&#xff0c;可提供 256 至 6864 个查找表 (LUT) 的密度。MachXO2 系列 PLD 提供多种特性&#xff0c;例如嵌入式块 RAM (EBR)、分布式 RAM 和用户闪存 (UFM…

linux端口数量上限65535原因;linux服务端最大连接数量可以超过65535

概述 关于端口数量&#xff0c;大家都知道最多是65535个端口。 这个来源于标识端口号的变量是16位的&#xff0c;那么就是65536个&#xff0c;去掉0这个特殊端口&#xff0c;剩下65535个&#xff0c;所以理论上最大可用数量是65535。 但是实际中还有一些特殊端口已经定义好用…

sourceTree代码回滚

记一次惊心动魄的代码回滚记录&#xff01; 背景&#xff1a; 因为test分支进行Jenkins代码构建的时候发现文件引入的两个已安装的依赖没有找到&#xff0c;构建报错 可是我明明已经安装成功&#xff0c;并且package.json中也有了版本记录&#xff0c;可就是构建失败&#xf…

面试官:“同学,你做的这几个项目都不错。但怎么问QPS你就胡说呢?”

作者&#xff1a;小傅哥 博客&#xff1a;https://bugstack.cn 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01;&#x1f604; 这位同学&#xff0c;你比上一位面试者好多了&#xff0c;你的简历中做的几个项目都不错。既有业务项目&#xff0c;也有技术项目…

电子签名软件有哪些?10大电子签名平台盘点

目录 一、电子签名软件有哪些 1.e签宝&#xff1a;国内签名领域老大哥 2.上上签&#xff1a;开创SaaS电子签极简模式 3.法大大&#xff1a;数智化签约管理平台 4.数字认证&#xff1a;中国电子认证第一股 5.契约锁&#xff1a;主攻中大型客户无缝集成各类系统 6.安心签&…

持有NPDP产品经理证书可以加薪吗?

NPDP(New Product Development Professional)是指产品经理国际资格认证&#xff0c;由美国产品开发与管理协会&#xff08;PDMA&#xff09;所发起的唯一国际公认新产品开发专业认证。NPDP是集理论、方法与实践为一体的全方位知识体系&#xff0c;为公司组织层级进行规划、决策…

破圈丨2023年绿色积分消费返利:云联惠3.0升级版【循环购】商业模式

破圈丨2023年绿色积分消费返利&#xff1a;云联惠3.0升级版【循环购】商业模式 京东供应链商品/自营商品/供应商商品 平台上面产品超过300w款产品&#xff0c;均为京东供应链货品&#xff0c;由京东统一仓储和配送&#xff0c;从源头上面杜绝假冒伪劣产品的存在&#xff0c;然…

AI绘图是什么技术?前景如何?

大家好&#xff0c;我是权知星球&#xff0c;人工智能最近大火&#xff0c;各大应用领域层出不穷&#xff0c;今天跟大家讨论一下AI绘图是什么技术&#xff1f;前景如何&#xff1f; 人工智能绘图是什么&#xff1f; 人工智能绘画是指利用人工智能进行绘画的过程&#xff0c;属…

redhat 6.4安装oracle11g RAC (三)

为数据和快速恢复去创建ASM磁盘组 只在节点rac1执行即可&#xff0c;进入grid用户下 [gridrac1 grid]$ srvctl status asm -a ASM is running on rac2,rac1 ASM is enabled. [gridrac1 grid]$ ps -ef|grep lsnr|grep -v grep|grep -v ocfs|awk {print$9} LISTENER_SCAN1 LISTE…

Databend 开源周报 第 98 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 后台服务 Dat…

强化学习从基础到进阶-案例与实践[2]:马尔科夫决策、贝尔曼方程、动态规划、策略价值迭代

【强化学习原理项目专栏】必看系列&#xff1a;单智能体、多智能体算法原理项目实战、相关技巧&#xff08;调参、画图等、趣味项目实现、学术应用项目实现 专栏详细介绍&#xff1a;【强化学习原理项目专栏】必看系列&#xff1a;单智能体、多智能体算法原理项目实战、相关技巧…