面试官:说说你对事件循环的理解
一、事件循环是什么
首先,JavaScript
是一门单线程的语言,意味着同一时间内只能做一件事,但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环
在JavaScript
中,所有的任务都可以分为
-
同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行
-
异步任务:异步执行的任务,比如
ajax
网络请求,setTimeout
定时函数等
同步任务与异步任务的运行流程图如下:
从上面我们可以看到,同步任务进入主线程,即主执行栈,异步任务进入任务队列,主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。上述过程的不断重复就事件循环
二、宏任务与微任务
如果将任务划分为同步任务和异步任务并不是那么的准确,举个例子:
console.log(1)setTimeout(()=>{console.log(2)
}, 0)new Promise((resolve, reject)=>{console.log('new Promise')resolve()
}).then(()=>{console.log('then')
})console.log(3)
如果按照上面流程图来分析代码,我们会得到下面的执行步骤:
console.log(1)
,同步任务,主线程中执行setTimeout()
,异步任务,放到Event Table
,0 毫秒后console.log(2)
回调推入Event Queue
中new Promise
,同步任务,主线程直接执行.then
,异步任务,放到Event Table
console.log(3)
,同步任务,主线程执行
所以按照分析,它的结果应该是 1
=> 'new Promise'
=> 3
=> 2
=> 'then'
但是实际结果是:1
=>'new Promise'
=> 3
=> 'then'
=> 2
出现分歧的原因在于异步任务执行顺序,事件队列其实是一个“先进先出”的数据结构,排在前面的事件会优先被主线程读取
例子中 setTimeout
回调事件是先进入队列中的,按理说应该先于 .then
中的执行,但是结果却偏偏相反
原因在于异步任务还可以细分为微任务与宏任务
微任务
一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前
常见的微任务有:
-
Promise.then
-
MutaionObserver
-
Object.observe(已废弃;Proxy 对象替代)
-
process.nextTick(Node.js)
宏任务
宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求就不太符合
常见的宏任务有:
- script (可以理解为外层同步代码)
- setTimeout/setInterval
- UI rendering/UI事件
- postMessage、MessageChannel
- setImmediate、I/O(Node.js)
这时候,事件循环,宏任务,微任务的关系如图所示
按照这个流程,它的执行机制是:
- 执行一个宏任务,如果遇到微任务就将它放到微任务的事件队列中
- 当前宏任务执行完成后,会查看微任务的事件队列,然后将里面的所有微任务依次执行完
回到上面的题目
console.log(1)
setTimeout(()=>{console.log(2)
}, 0)
new Promise((resolve, reject)=>{console.log('new Promise')resolve()
}).then(()=>{console.log('then')
})
console.log(3)
流程如下
// 遇到 console.log(1) ,直接打印 1
// 遇到定时器,属于新的宏任务,留着后面执行
// 遇到 new Promise,这个是直接执行的,打印 'new Promise'
// .then 属于微任务,放入微任务队列,后面再执行
// 遇到 console.log(3) 直接打印 3
// 好了本轮宏任务执行完毕,现在去微任务列表查看是否有微任务,发现 .then 的回调,执行它,打印 'then'
// 当一次宏任务执行完,再去执行新的宏任务,这里就剩一个定时器的宏任务了,执行它,打印 2
三、async与await
async
是异步的意思,await
则可以理解为 async wait
。所以可以理解async
就是用来声明一个异步方法,而 await
是用来等待异步方法执行
async
async
函数返回一个promise
对象,下面两种方法是等效的
function f() {return Promise.resolve('TEST');
}// asyncF is equivalent to f!
async function asyncF() {return 'TEST';
}
await
正常情况下,await
命令后面是一个 Promise
对象,返回该对象的结果。如果不是 Promise
对象,就直接返回对应的值
async function f(){// 等同于// return 123return await 123
}
f().then(v => console.log(v)) // 123
不管await
后面跟着的是什么,await
都会阻塞后面的代码
async function fn1 (){console.log(1)await fn2()console.log(2) // 阻塞
}async function fn2 (){console.log('fn2')
}fn1()
console.log(3)
上面的例子中,await
会阻塞下面的代码(即加入微任务队列),先执行 async
外面的同步代码,同步代码执行完,再回到 async
函数中,再执行之前阻塞的代码
所以上述输出结果为:1
,fn2
,3
,2
四、流程分析
通过对上面的了解,我们对JavaScript
对各种场景的执行顺序有了大致的了解
这里直接上代码:
async function async1() {console.log('async1 start')await async2()console.log('async1 end')
}
async function async2() {console.log('async2')
}
console.log('script start')
setTimeout(function () {console.log('settimeout')
})
async1()
new Promise(function (resolve) {console.log('promise1')resolve()
}).then(function () {console.log('promise2')
})
console.log('script end')
分析过程:
- 执行整段代码,遇到
console.log('script start')
直接打印结果,输出script start
- 遇到定时器了,它是宏任务,先放着不执行
- 遇到
async1()
,执行async1
函数,先打印async1 start
,下面遇到await
怎么办?先执行async2
,打印async2
,然后阻塞下面代码(即加入微任务列表),跳出去执行同步代码 - 跳到
new Promise
这里,直接执行,打印promise1
,下面遇到.then()
,它是微任务,放到微任务列表等待执行 - 最后一行直接打印
script end
,现在同步代码执行完了,开始执行微任务,即await
下面的代码,打印async1 end
- 继续执行下一个微任务,即执行
then
的回调,打印promise2
- 上一个宏任务所有事都做完了,开始下一个宏任务,就是定时器,打印
settimeout
所以最后的结果是:script start
、async1 start
、async2
、promise1
、script end
、async1 end
、promise2
、settimeout
相关文章:

面试官:说说你对事件循环的理解
一、事件循环是什么 首先,JavaScript是一门单线程的语言,意味着同一时间内只能做一件事,但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环 在JavaScript中,所有的任务都可以分为 同步任务&#…...

【SpringCloud微服务实战03】Nacos 注册中心
一、Nacos安装 官方文档安装Nacos教程:Nacos 快速开始 这里安装的是1.4.7版本,安装之后访问http://127.0.0.1:8848/nacos 管理界面如下:(用户名:nacos,密码:nacos) 二、Nacos服务注册和发现 1、在父工程中配置文件pom.xml 中添加spring-cloud-alilbaba的管理依赖:…...

FLatten Transformer_ Vision Transformer using Focused Linear Attention
paper: https://arxiv.org/abs/2308.00442 code: https://github.com/LeapLabTHU/FLatten-Transformer 摘要 当将transformer模型应用于视觉任务时,自注意的二次计算复杂度( n 2 n^2 n2)一直是一个持续存在的挑战。另一方面,线性注意通过精心设计的映射…...

STM32CubeMX学习笔记17--- FSMC
1.1 TFTLCD简介 TFT-LCD(thin film transistor-liquid crystal display)即薄膜晶体管液晶显示器。液晶显示屏的每一个像素上都设置有一个薄膜晶体管(TFT),每个像素都可以通过点脉冲直接控制,因而每个节点都…...
【MogDB】实战MogDB数据库适配Halo博客系统1.6版本(基于springframework+hibernate+HikariPool)
前言 前一篇文章说了MogDB适配Halo,【MogDB】将流行的博客系统Halo后端的数据库设置为MogDB,但是适配的是2.x版本,由于2.x版本已经引入了对postgresql的支持,而MogDB对于postgresql有很好的兼容性,因此适配起来很简单。但是由于halo2.x的版本…...

Python与FPGA——局部二值化
文章目录 前言一、局部二值化二、Python局部二值化三、FPGA局部二值化总结 前言 局部二值化较全局二值化难,我们将在此实现Python与FPGA的局部二值化处理。 一、局部二值化 局部二值化就是使用一个窗口,在图像上进行扫描,每扫出9个像素求平均…...

shell文本处理工具-shell三剑客1
shell脚本常用基础命令2 shell脚本常用基础命令 shell脚本常用基础命令2一、grep用法二、sed用法2.1p参数 (显示)n参数(只显示处理过的行) 文本处理三剑客:grep sed awk 一、grep用法 grep -E egrep (扩展搜索正文表…...

函数的传入参数-传参定义
基于函数的定义语法: def 函数名(传入参数)函数体return 返回值 可以有如下函数定义: def add(x,y):return xyprint(f"{x} {y}的结果是:{result}") 实现了,每次计算的是xy,而非…...

主流接口测试框架对比,究竟哪个更好用
公司计划系统的开展接口自动化测试,需要我这边调研一下主流的接口测试框架给后端测试(主要测试接口)的同事介绍一下每个框架的特定和使用方式。后端同事根据他们接口的特点提出一下需求,看哪个框架更适合我们。 需求 1、接口编写…...
常用python模板
1.简单脚本模板 def main():#代码逻辑if __name__"__main__":main() 2.类定义模板 Class Myclass:def __init__(self,parameter):self.parameterparameterdef my_method(self):#方法逻辑 3.函数定义模板 def my_function(parameter):#代码逻辑return result 4.…...
53. 最大子数组和(力扣LeetCode)
文章目录 53. 最大子数组和题目描述暴力(运行超时)贪心 53. 最大子数组和 题目描述 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 子数组是数组…...
如何远程SSH连接在家的服务器主机
当您需要通过SSH远程连接到家里的服务器主机时,以下是更详细的实施步骤: 1. 确保服务器主机已开启SSH服务 安装SSH服务:首先,确保您的服务器主机上安装了SSH服务。根据您的操作系统,您可以使用相应的包管理器来安装。…...

【亲测有效】解决三月八号ChatGPT 发消息无响应!
背景 今天忽然发现 ChatGPT 无法发送消息,能查看历史对话,但是无法发送消息。 可能的原因 出现这个问题的各位,应该都是点击登录后顶部弹窗邀请 [加入多语言 alapha 测试] 了,并且语言选择了中文,抓包看到 ab.chatg…...

vue结合vue-electron创建应用程序
这里写自定义目录标题 安装electron第一种方式:vue init electron-vue第二种方式:vue add electron-builder 启动electron调试功能:background操作和使用1、覆盖窗口的菜单上下文、右键菜单2、监听关闭事件、阻止默认行为3、创建悬浮窗口4、窗…...

【C++】STL(二) string容器
一、string基本概念 1、本质 string是C风格的字符串,而string本质上是一个类 string和char * 区别: char * 是一个指针 string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器。 2、特点 1、stri…...

PyCM:Python中的混淆矩阵库
PyCM:Python中的混淆矩阵库 在机器学习和数据科学领域,评估模型的性能是至关重要的。混淆矩阵是一种常用的评估工具,用于可视化和量化分类模型的预测结果。PyCM是一个开源的Python库,提供了丰富的功能来计算和分析混淆矩阵。本文将…...

Day22:安全开发-PHP应用留言板功能超全局变量数据库操作第三方插件引用
目录 开发环境 数据导入-mysql架构&库表列 数据库操作-mysqli函数&增删改查 数据接收输出-html混编&超全局变量 第三方插件引用-js传参&函数对象调用 完整源码 思维导图 PHP知识点: 功能:新闻列表,会员中心࿰…...
IOS面试题object-c 61-70
61. 阐述isKindOfClass、isMemberOfClass、selector作用分别是什么?isKindOfClass:作用是某个对象属于某个类型或者继承自某类型。 isMemberOfClass:某个对象确切属于某个类型。 selector:通过方法名,获取在内存中的函…...
Git指令reset的参数soft、mixed与hard三者之间的区别
主要内容 reset默认不写参数,与使用mixed参数含义一样 为了描述简洁,使用下图说明: #mermaid-svg-LtChquRXlEV1j6og {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-LtChquRXlEV1j…...
RGMII 接口调试
目录 硬件检查 软件检查 调试步骤 硬件检查 硬件工程师检查原理图和PCB,核查RGMII线路连接是否正确,PHY的 TX连接对端 RX,PHY的RX连接对端TX,原理图上以引脚序号引脚名 引脚类型(输入还是输出)逐一核查RGMII接口各个网络&#…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...

DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...

关于easyexcel动态下拉选问题处理
前些日子突然碰到一个问题,说是客户的导入文件模版想支持部分导入内容的下拉选,于是我就找了easyexcel官网寻找解决方案,并没有找到合适的方案,没办法只能自己动手并分享出来,针对Java生成Excel下拉菜单时因选项过多导…...

华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...