事件循环(Event Loop)

2023-11-12

目录

一、浏览器的进程模型

1.1 进程

1.2 线程

1.3 浏览器的进程和线程 

二、渲染主线程

 2.1 渲染主线程中为什么使用异步

 2.2 js的异步*

2.3 队列的优先级*

 添加任务到微队列的主要⽅式主要是使⽤ Promise、NextTick、MutationObserver(这个不了解)

Promise.resolve().then(函数)

2.4 JS的事件循环

⾯试题:JS 中的计时器能做到精确计时吗?为什么?


一、浏览器的进程模型

        在了解事件循环之前,我们先看看浏览器的进程模型

1.1 进程

        程序运⾏需要有它⾃⼰专属的内存空间,可以把这块内存空间简单的理解为进程。

 

         每个应⽤⾄少有⼀个进程,进程之间相互独⽴,即使要通信,也需要双⽅

同意。

1.2 线程

        有了进程后,就可以运⾏程序的代码了。 运⾏代码的「⼈」称之为「线程」。

⼀个进程⾄少有⼀个线程,所以在进程开启后会⾃动创建⼀个线程来运⾏代码,该线程称之为主线程。

        如果程序需要同时执⾏多块代码,主线程就会启动更多的线程来执⾏代码,所以⼀个进程中可以包含多个线程。

1.3 浏览器的进程和线程 

        浏览器是一个多进程多线程的引用程序。

        浏览器内部⼯作极其复杂,为了避免相互影响,为了减少连环崩溃的⼏率,当启动浏览器后,它会⾃动启动多个进程。

         可以在浏览器的任务管理器中查看当前的所有进程

        

 其中,最主要的进程有:

  1. 浏览器进程         
            
    主要负责界⾯显示、⽤户交互、⼦进程管理等。浏览器进程内部会启动多个线程处理不同的任务。
  2. ⽹络进程 
    负责加载⽹络资源。⽹络进程内部会启动多个线程来处理不同的⽹络任务。
  3. 渲染进程(主要了解的进程)
    渲染进程启动后,会开启⼀个渲染主线程,主线程负责执⾏ HTMLCSS、 JS 代码。默认情况下,浏览器会为每个标签⻚开启⼀个新的渲染进程,以保证不同的标签⻚之间不相互影响。

二、渲染主线程

        渲染主线程是浏览器中最繁忙的线程,需要它处理的任务包括但不限于:       
  • 解析 HTML
  • 解析 CSS
  • 计算样式
  • 布局
  • 处理图层
  • 每秒把⻚⾯画 60
  • 执⾏全局 JS 代码
  • 执⾏事件处理函数
  • 执⾏计时器的回调函数
  • 。。。。。。
  1. .在最开始的时候,渲染主线程会进⼊⼀个⽆限循环
  2. 每⼀次循环会检查消息队列中是否有任务存在。如果有,就取出第⼀个任务执⾏,执⾏完⼀个后进⼊下⼀次循环;如果没有,则进⼊休眠状态。
  3. 其他所有线程(包括其他进程的线程)可以随时向消息队列添加任务。新任务会加到消息队列的末尾。在添加新任务时,如果主线程是休眠状态,则会将其唤醒以继续循环拿取任务
    整个过程,被称之为事件循环(消息循环)

 2.1 渲染主线程中为什么使用异步

  1. 代码在执⾏过程中,会遇到⼀些⽆法⽴即处理的任务,⽐如:
    计时完成后需要执⾏的任务
    —— setTimeout setInterval
    ⽹络通信完成后需要执⾏的任务 -- XHR Fetch
    ⽤户操作后需要执⾏的任务 -- addEventListener
  2. 如果让渲染主线程等待这些任务的时机达到,就会导致主线程⻓期处于
  3. 「阻塞」的状态,从⽽导致浏览器「卡死」
        如果让渲染主线程等待这些任务的时机达到,就会导致主线程⻓期处于「阻塞」的状态,从⽽导致浏览器「卡死」
        渲染主线程承担着极其重要的⼯作,⽆论如何都不能阻塞!

        因此,浏览器选择异步来解决这个问题
 
        使⽤异步的⽅式,渲染主线程永不阻塞

 2.2 js的异步*

        JS是⼀⻔单线程的语⾔,这是因为它运⾏在浏览器的渲染主线程中,⽽渲染主线程只有⼀个。
        ⽽渲染主线程承担着诸多的⼯作,渲染⻚⾯、执⾏ JS 都在其中运⾏。
        如果使⽤同步的⽅式,就极有可能导致主线程产⽣阻塞,从⽽导致消息队列中的很多其他任务⽆法得到执⾏。这样⼀来,⼀⽅⾯会导致繁忙的主线程⽩⽩的消耗时间,另⼀⽅⾯导致⻚⾯⽆法及时更新,给⽤户造成卡死现象。
        所以浏览器采⽤异步的⽅式来避免。具体做法是当某些任务发⽣时,⽐如计时器、⽹络、事件监听,主线程将任务交给其他线程去处理,⾃身⽴即结束任务的执⾏,转⽽执⾏后续代码。当其他线程完成时,将事先传递的回调函数包装成任务,加⼊到消息队列的末尾排队,等待主线程调度执⾏。
        在这种异步模式下,浏览器永不阻塞,从⽽最⼤限度的保证了单线程的流畅
运⾏。

2.3 队列的优先级*

        任务没有优先级,在消息队列中先进先出,消息队列是有优先级的.
        
根据 W3C 的最新解释:

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

随着浏览器的复杂度急剧提升,W3C 不再使⽤宏队列的说法

 在⽬前 chrome 的实现中,⾄少包含了下⾯的队列:

  • 延时队列:⽤于存放计时器到达后的回调任务,优先级「中」
  • 交互队列:⽤于存放⽤户操作后产⽣的事件处理任务,优先级「⾼」
  • 微队列:⽤户存放需要最快执⾏的任务,优先级「最⾼」

 添加任务到微队列的主要⽅式主要是使⽤ Promise、NextTick、MutationObserver(这个不了解)

// ⽴即把⼀个函数添加到微队列

Promise.resolve().then(函数)

 

        先走全局JS,在执行微队列里的任务,在执行延时队列的任务依次按此优先级往下。

2.4 JS的事件循环

        事件循环⼜叫做消息循环,是浏览器渲染主线程的⼯作⽅式。
        在 Chrome 的源码中,它开启⼀个不会结束的
for 循环,每次循环从消息队列中取出第⼀个任务执⾏,⽽其他线程只需要在合适的时候将任务加⼊到队列末尾即可。

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

        根据 W3C 官⽅的解释,每个任务有不同的类型,同类型的任务必须在同⼀个队列,不同的任务可以属于不同的队列。不同任务队列有不同的优先级, 在⼀次事件循环中,由浏览器⾃⾏决定取哪⼀个队列的任务。但浏览器必须有⼀个微队列,微队列的任务⼀定具有最⾼的优先级,必须优先调度执⾏。

⾯试题:JS 中的计时器能做到精确计时吗?为什么?

 不⾏,因为:

  1. 计算机硬件没有原⼦钟,⽆法做到精确计时
  2. 操作系统的计时函数本身就有少量偏差,由于 JS 的计时器最终调⽤的是操作系统的函数,也就携带了这些偏差
  3. 按照 W3C 的标准,浏览器实现计时器时,如果嵌套层级超过 5 层,则会带有 4 毫秒的最少时间,这样在计时时间少于 4 毫秒时⼜带来了偏差
  4. 受事件循环的影响,计时器的回调函数只能在主线程空闲时运⾏,因此⼜带来了偏差
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

事件循环(Event Loop) 的相关文章

  • 如何在 AngularJS 中使用 $timeout 运行带有参数的函数?

    我的 AngularJS 控制器中有这个函数 看起来像这样 polling interval 1000 var poll function Execution code timeout poll polling interval poll
  • 使用最新 Ember Data 版本中的 RESTSerializer 格式化 JSON

    我正在努力将我的 JSON munge 成正确的格式 为了说明这一点 我做了一个快速的 JSfiddle http jsfiddle net chrismasters NQKvy 638 http jsfiddle net chrismas
  • 从数组数组中获取唯一值[重复]

    这个问题在这里已经有答案了 我有以下数组 let arr email protected cdn cgi l email protection email protected cdn cgi l email protection email
  • Yeoman-Angular 生成的应用程序中缺少 Angular 脚本

    我已经使用 Yeoman Angular Generator 生成了一个应用程序 但项目中缺少 angular js 和其他 Angular 文件 我可以在 Bower json 文件中看到这些依赖项 如下所示 name mi portfo
  • Object.assign() - 奇怪的行为需要解释

    我有这个代码 function margeOptions options passedOptions options Object assign options passedOptions let passedOpts a true let
  • Vue js按钮冻结dom

    我试图在按下按钮时切换包含加载动画的跨度 直到使用 v if 函数完成 但是当我按下按钮时 DOM 冻结并且 span 元素保持不变 直到函数调用结束 如何让 DOM 不冻结并显示加载图标 非阻塞按钮按下可能是一个解决方案 HTML
  • 在 Javascript 中,使用 var foo = function foo(i) { ... } 的动机或优点是什么?

    我在答案中看到 在Javascript中 为什么要写 var QueryStringToHash function QueryStringToHash query https stackoverflow com questions 3233
  • 赋予 d3 序数轴标签与尺度名称不同

    我有一个序数scale具有不同值的某些标签 我想显示该比例的轴 其中轴标签与比例标签不同 我有这个代码 var width 1000 var height 600 var margins left 100 40 right 25 botto
  • Angular 7 Guard 重定向仅适用于双击

    问题是我已经实现了一个 Guard 旨在处理特定的目录 如果当前用户名的角色等于 2 它应该返回 true 如果没有 那么它不应该重定向 这是我的 app routing module ts 文件 问题出在 userlist 路径中 我们是
  • 使用 getElementById 在 javascript 中使用正则表达式进行 Html 表单验证?

    我想使用正则表达式验证 html 表单的示例模式 AAA 111 2222 aa 1234 目前 我的代码要么为所有输入返回 正确 要么为所有输入返回 不正确 并且我无法弄清楚我的问题出在哪里 var x document getEleme
  • 如何正确关闭 Node.js Express 服务器?

    我需要在收到回调后关闭服务器 auth github callback网址 与平常一样HTTP API http nodejs org docs latest api http html关闭 服务器目前支持server close call
  • 函数声明或函数表达式

    我刚刚在块作用域中定义函数时遇到了问题 考虑以下程序 try greet function greet alert Merry Christmas catch error alert error 我希望这个程序能够发出警报Merry Chr
  • 从 UnityWebGL jslib 返回字符串

    我想使用 jslib 来获取网址参数 像这样的代码 jslib GetUrl function var s var strUrl window location search var getSearch strUrl split var g
  • 如何让无限滚动发挥作用?

    我正在尝试让这个无限加载脚本在我的项目中工作 这是我的 HTML div div div class pagina div div class pagina div div class pagina div div class pagina
  • NodeJS 中的缩进多行日志记录

    我要打印JSON stringify d 反对控制台 将上下文作为 Mocha 测试套件输出的一部分 当测试缩进时 我希望对象日志行向右缩进足够远 例如 3 4 个制表符空格 以便它们可以识别地位于右侧describe group 我怎样才
  • 如何在 Astro 中的组件之间共享状态?

    我相信我在代码中采用了错误的方法 如何在按钮单击中设置客户端首选项 该按钮单击用作全局 astro 组件中的道具 或者我应该怎么做 我知道这是可能的 因为 astro js 本身在他们的文档网站中这样做了 下面是我的尝试的解释 我目前正在开
  • 是否可以从 webpack 中的文件名中删除特殊字符?

    长话短说 我的资产文件名中不能包含某些字符 例如连字符 我没有运气通过解析 webpack 文档来弄清楚是否可以使用正则表达式或类似的东西重命名文件 这样我就可以从我无法控制源文件名的 3rd 方包中删除任何连字符 我的超级天真的例子是这样
  • 使用重复模式捕获正则表达式

    我试图捕获字符串的所有部分 但我似乎无法正确处理 该字符串具有以下结构 1 22 33 中间有运算符的数字 可以有任意数量的术语 我想要的是 1 22 33 1 22 33 但我得到 1 22 33 22 33 我尝试过各种正则表达式 这是
  • 相当于 JavaScript 中 Ruby 的each_cons

    许多语言都曾提出过这个问题 但 javascript 却没有 Ruby 有方法Enumerable each cons https devdocs io ruby 2 5 enumerable method i each cons看起来像这
  • 通过jquery ajax()和serialize()提交html表单

    我想通过 jquery ajax 提交此表单 这是我所做的 但它不起作用 即表单正在提交并刷新页面 但我没有看到响应 即在同一页面上打印数组 HTML

随机推荐

  • dubbo优雅停机

    dubbo优雅停机 Dubbo是通过JDK的ShutdownHook来完成优雅停机的 所以如果用户使用 kill 9 PID 等强制关闭指令 是不会执行优雅停机的 只有通过 kill PID 时 才会执行 原理 服务提供方 停止时 先标记为
  • grafana与prometheus实现监控可视化

    1 Grafana基础知识 Grafana是一个开源的指标监测和可视化工具 官方网站为 Grafana The open observability platform Grafana Labs 常用于展示基础设施的时序数据和应用程序运行分析
  • 大话设计模式9—观察者模式(通知者与观察者)

    大话设计模式9 观察者模式 老板回来 我不知道 1 需求 老板回来 我不知道 2 双向耦合的设计 2 1 前台秘书类 2 2 看股票同事类 2 3main函数及输出 3 解耦修改 3 1 抽象观察者类 3 2 前台秘书类 3 3 main函
  • Java编程中出现乱码的原因

    乱码的原因 理解了编码 我们来看乱码 乱码有两种常见原因 一种比较简单 就是简单的解析错误 另外一种比较复杂 在错误解析的基础上进行了编码转换 我们分别介绍 1 解析错误 看个简单的例子 一个法国人采用Windows 1252编码写了个文件
  • Windows/PC(win + R) 电脑常见操作命令50条

    摘要 win R 1 cmd 打开终端 2 gpedit msc 本地组策略编辑器 3 Nslookup IP地址侦测器 4 explorer 文件资源管理器 5 notepad 系统默认记事本 6 cleanmgr 磁盘清理 7 serv
  • 微信公众号运营错误的四个方式

    1 很多广告宣传 很多的微信公众平台注册便是为了更好地宣传策划商品 在开展內容輸出的情况下 沒有立在客户的视角开展內容輸出 消息推送的內容可能是七拼八凑 与微信公众号的精准定位偏移很远 要想取得成功的运营公众号 最先要做的便是深层次发掘总体
  • vue3 + vite 在线预览docx, pdf, pptx(内外网)并实现移动端适配

    一 内网 1 docx 使用docx preview 安装插件 npm i docx preview S 引入依赖 docx import renderAsync from docx preview let docx import meta
  • GO终端读取

    GO终端读取 Go语言获取标准输入 Go语言 fmt 包下有 fmt Scan fmt Scanf fmt Scanln 三个函数 可以在程序运行过程中获取用户输入 func Scan a interface n int err error
  • Spring核心之一:IOC

    IOC Inversion of Control 其实是一种思想 这种思想并不是Spring独有的 而是在软件开发中 大家提出的一种开发原则 类似面向接口编程原则 开闭原则等 网上有很多类似的文章尝试去通俗易懂地解释IOC思想 这里我根据自
  • BugkuCTF-Crypto题小山丘的秘密

    本题考查希尔密码 解题流程 题目信息 1 根据提示知道是希尔 hill 密码 解密网站 www atoolbox net Tool php Id 914 ac csdn flag txt 里给出A 1 一般的希尔密码是A 0 B 1 C 2
  • Vue.js 学习笔记 第5章 内置指令

    本篇目录 5 1 基本指令 5 2 条件渲染指令 5 3 列表渲染指令 v for 5 4 方法与事件 5 5 实战 利用计算属性 指令等知识开发购物车 回顾一下第2 2节 我们己经介绍过指令 Directive 的概念了 Vue js的指
  • JAVA 【基础】 log4j 输出样式

    先了解一下log4j 的打印参数如下 参数 说明 L 输出代码中的行号 l 输出日志事件的发生位置 包括类目名 发生的线程 以及在代码中的行数 如 Testlog main TestLog java 10 m 输出代码中指定的消息 p 输出
  • 使用vs2013编译qt4库

    一 废话 由于项目需要搭建qt msvc2013的环境 而官方提供的qt4库最高只支持和vs2010关联 如果需要配置qt msvc2010只需要安装官方给的qt4库的安装包 然后下载addin就可以了 本人也是第一次搭建该环境 在网上找了
  • GDB 程序调试常用命令

    调试之前 若要在GDB中调试程序在编译时需要加上调试信息 在GCC中添加的方法 GCC g a c o a exe 或下面提供更符合GDB的调试信息 GCC ggdb a c o a exe 运行流程 命令 作用 start 开始执行程序
  • elasticsearch评分进阶

    elasticsearch 评分进阶 原文引用自 Advanced Scoring in elasticsearch 作者还有一篇Elasticsearch评分的经验说明 建议爬墙参考slideshare上的资源对照查看 如有侵权请联系 s
  • 几张架构图阐述微服务架构治理相关知识点

    微服务架构的技术点 超时时间设置 接口重试机制 流量QPS限流 请求熔断处理 请求降级处理 今天给大家分享一下以上相关的知识点 通过微服务系统之间RPC通信的方式 以架构图的形式给大家展开介绍 1 超时时间设置 应用访问业务系统A 业务系统
  • 程序员都要学学任正非的坚强!这个老头,在逆境中崛起!

    点赞再看 养成习惯 微信搜一搜 findyi 关注这个喜欢写情怀的程序员 回复 1 获得程序员职场晋升PPT一份 2019年的最后一天 罗胖在跨年演讲中引用了何帆老师的一句话 用一个人的长期主义 对冲世界的不确定性 那时候 谁也无法想到 世
  • 【STM32】为什么STM32的Flash地址要设置到0x08000000

    参考 不是问题的问题 为什么STM32的Flash地址要设置到0x08000000 这边涉及到分散加载文件 启动文件等等 先挖坑 搞定IAP升级以后再填 为什么STM32的Flash地址要设置到0x08000000 主flash启动时 是从
  • tomcat多系统部署方案

    多系统部署到一个 Tomcat 中 如果某一个系统崩溃可能会同时使其他系统不能正常工作 因为它们运行在同一个 JVM 上 就需要在同一个服务器中安装多个 Tomcat 来运行不同的 WEB 系统 一 Tomcat 版本选择 安装 JDK 版
  • 事件循环(Event Loop)

    目录 一 浏览器的进程模型 1 1 进程 1 2 线程 1 3 浏览器的进程和线程 二 渲染主线程 2 1 渲染主线程中为什么使用异步 2 2 js的异步 2 3 队列的优先级 添加任务到微队列的主要 式主要是使 Promise NextT