js——事件循环
JS—事件循环
js运行的环境称之为宿主环境。
执行栈 :call stack ,一个数据结构,用于存放各种函数的执行环境,每一个函数执行之前他的相关信息会加入到执行栈中,函数调用之前,创建执行环境,然后加入到执行栈中;函数调用之后,销毁执行环境
function a (){
console.log("a")
b()
}
function b(){
console.log("b")
c()
}
function c(){
console.log("c")
}
console.log("global")
a()
执行顺序: global a b c
答案显而易见,但是为什么会这样呢?
整个js运行了之后 就只有一个执行栈,首先整个js定义了三个函数,js代码在运行的时候都会先初始化一个全局上下文GO,然后把GO放入在call stack 中,接着console.log()函数执行,创建一个log的上下文放入call stack,输入“global”之后log的上下文就会销毁,然后a()函数执行,创建一个a的上下文放入call stack,输出"a",b()执行,这时a函数还没有结束,因为a函数里面还需要执行b函数,所以a的上下文还留在call stack中,b() 函数执行,创建一个b的执行上下文放入call stack 中,b函数出入"b",c(),这时b函数也还没有结束,还需要执行c()函数,所以b的上下文也还在call stack中没有销毁,c()函数执行,创建一个c的上下文放入到call stack中,c函数输出"c"后,c函数结束,call stack里面的c的上下文销毁,c函数执行完,b函数也就执行完了所以,接着,b的上下文也接着销毁,b函数执行完后a函数也就代表着执行完,所以a的上下文也接着被销毁,最后这整个js代码运行完后call stack里面最先初始化创建的全局上下文就销毁。
再来一个例子巩固一下上面的原理:求斐波拉契数列 第一个和第二个数固定为1,之后任意一个数都是前两位数之和
function getFeibo(n){
if(n ===1 || n===2 ){
return 1;
}
return getFeibo(n-1) + getFeibo(n-2)
}
console.log(getFeibo(3))
首先初始化一个全局上下文放入call stack 中 log()执行 创建一个log的执行上下文放入call stack 中 这时getFeibo(3)执行,创建一个n=3的getFeibo的执行上下文,n=3没有进入if语句,执行getFeibo(3-1)函数,创建一个n=2 的getFeibo的执行上下文,进入if语句 返回1,n=2的getFeibo的执行上下文销毁,return的前部分结束,执行 getFeibo(3-2)函数,创建一个 n=1 的getFeibo的执行上下文, 进入if语句,返回1,n=1的getFeibo的执行上下文销毁,return的后部分结束 n=3的getFeibo函数运行结束返回2 ,n=3的getFeibo的执行上下文销毁,最后初始化的那个全局上下文也销毁这时call stack清空。
js引擎永远执行的都是执行栈的最顶部
异步函数:某些函数不会立即执行,需要等到某个时机到达后才会执行,这样的函数就被称之为异步函数,比如时间处理函数。异步函数的执行时机,会被宿主环境控制。
浏览器宿主环境中包含有5个线程:
- JS引擎:负责执行执行栈的最顶部代码
- GUI线程:负责渲染页面
- 事件监听线程:负责监听各种事件
- 计时线程:负责计时(setTimeout、setInterval)
- 网络线程:负责网络通信 如 ajax
当上面的线程发生了某些事情,如果该线程发现,这件事情有处理程序,他就会将该处理程序放入到一个叫做事件队列的的内存里,当JS引擎发现,执行栈call stack 中里面已经没有任何内容后,call stack就会把事件队列中的第一个函数加入到执行栈中去执行
例如:
function a(){
console.log("a")
setTimeout(()=>{
b()
},0)
c()
}
function b(){
console.log("b")
}
function c(){
console.log("c")
}
console.log("global")
a()
输出顺序:global a c b
首先初始化一个全局执行上下午放入call stack中 log()函数执行,创建一个log的执行上下文,输出global,log的执行上下文销毁,a函数执行,创建一个a的执行上下文,log执行创建一个log的执行上下文,输出a,log的执行上下文销毁,setTimeout 开启一个定时器告诉宿主环境0秒后执行b函数,注意这时b函数还没有执行,就紧接着执行c函数
创建一个c的执行上下文,log执行创建一个log的执行上下文,输出c,log的执行上下文销毁,这时a函数里面的代码执行完 a的执行上下文被销毁,整个js代码执行完毕,全局上下文也从call stack被销毁,call stack清空。这时有人会问 诶 b函数不是还没有执行吗怎么说整个js代码执行完毕了呢? 是这样的 setTimeout开启了一个定时器,这时已经是计时线程发现了有处理程序,会告诉宿主环境 0 秒后执行b函数,宿主环境知道后就会在0秒过后把 b函数放入到一个叫事件队列的内存中。所以当call stack 里面的内容清空后(即当最开始初始化的全局上下文被销毁后)JS引擎会从事件队列中取出处理程序来执行。
JS引擎从事件队列中取出处理程序来执行,以及与宿主环境的配合,称之为事件循环
事件队列在不同的宿主环境中有所差异,大部分宿主环境会将事件队列进行细分。在浏览器中,事件队列被分为两种:
- 宏队列:macroTask,计时器结束的回调、事件回调、http回调等等绝大部分异步函数是进入宏队列
- 微队列:MutationObserver,Promise产生的回调进入微队列
当执行栈清空是,JS引擎首先会将微任务中的所有任务一次执行结束,如果没有微任务,则执行宏任务。
js——事件循环的更多相关文章
- Node.js 事件循环(Event Loop)介绍
Node.js 事件循环(Event Loop)介绍 JavaScript是一种单线程运行但又绝不会阻塞的语言,其实现非阻塞的关键是“事件循环”和“回调机制”.Node.js在JavaScript的基 ...
- Node.js事件循环
Node JS是单线程应用程序,但它通过事件和回调概念,支持并发. 由于Node JS每一个API是异步的,作为一个单独的线程,它使用异步函数调用,以保持并发性.Node JS使用观察者模式.Node ...
- js事件循环机制辨析
对于新接触js语言的人来说,最令人困惑的大概就是事件循环机制了.最开始这也困惑了我好久,花了我几个月时间通过书本,打代码,查阅资料不停地渐进地理解他.接下来我想要和大家分享一下,虽然可能有些许错误的 ...
- 6、Node.js 事件循环
#########################################################################################Node.js 事件循 ...
- Node.js 事件循环
Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高. Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发. Node.j ...
- Node.js 学习(五)Node.js 事件循环
Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高. Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发. Node.j ...
- js事件循环
之前有看过一些事件循环的博客,不过一阵子没看就发现自己忘光了,所以决定来自己写一个博客总结下! 首先,我们来解释下事件循环是个什么东西: 就我们所知,浏览器的js是单线程的,也就是说,在同一时刻,最多 ...
- [译] 所有你需要知道的关于完全理解 Node.js 事件循环及其度量
原文地址:All you need to know to really understand the Node.js Event Loop and its Metrics 原文作者:Daniel Kh ...
- Node.js 事件循环机制
Node.js 采用事件驱动和异步 I/O 的方式,实现了一个单线程.高并发的 JavaScript 运行时环境,而单线程就意味着同一时间只能做一件事,那么 Node.js 如何通过单线程来实现高并发 ...
随机推荐
- 基于springboot工程浅谈整合rabbitmq怎么样防止消息发送mq不丢失和消费mq的消息防止丢失
本文只针对springboot整合rabbitmq的消息防丢失,话不多说,上干货.... 设置发送mq消息不丢失实现思路 执行的方案: 第一步,要对队列,消息以及交换机进行持久化操作(保存到物理磁盘中 ...
- 【字符串算法】AC自动机
国庆后面两天划水,甚至想接着发出咕咕咕的叫声.咳咳咳,这些都不重要!最近学习了一下AC自动机,发现其实远没有想象中的那么难. AC自动机的来历 我知道,很多人在第一次看到这个东西的时侯是非常兴奋的.( ...
- Java知识系统回顾整理01基础04操作符07Scanner
一.Scanner 需要用到从控制台输入数据时,使用Scanner类. 二.使用Scanner读取整数 注意: 使用Scanner类,需要在最前面加上 import java.util.Scanner ...
- 使用 .NET 进行游戏开发
微软是一家综合性的网络公司,相信这点来说不用过多的赘述,没有人不知道微软这个公司,这些年因为游戏市场的回报,微软收购了很多的游戏公司还有独立工作室,MC我的世界就是最成功的的案例,现在市值是排在全世界 ...
- Android和CraigDJ
下载HTML source - 46.8 KB 下载APK - 8.1 MB Introduction 大家好,欢迎来到我的Android应用程序项目! 我必须承认,我仍然对原生安卓环境几乎一无所知 ...
- Docker镜像仓库Harbor部署
一.Harbor组件 组件 功能 harbor-adminserver 配置管理中心 harbor-db Mysql数据库 harbor-jobservice 负责镜像复制 harbor-log 记录 ...
- 联赛模拟测试12 B. trade
题目描述 分析 \(n^2\) 的 \(dp\) 应该比较好想 设 \(f[i][j]\) 为当前在第 \(i\) 天剩余的货物数量为 \(j\) 时的最大收益 那么它可以由 \(f[i-1][j]\ ...
- 多测师讲解自动化 _rf 变量_高级讲师肖sir
rf变量 log 打印全局变量 列表变量: 字典变量: 查看当前工程下的变量 紫色表示变量名有误 设置全局变量 设置列表变量 设置字段变量 关键字书写格式问题
- jsoncpp笔记
Json Json是一种轻量级数据交换格式,可以表示数字,字符串,布尔值,null,数组,键值对: { "encoding" : "UTF-8", " ...
- 【源码项目+解析】C语言/C++开发,打造一个小项目扫雷小游戏!
一直说写个几百行的小项目,于是我写了一个控制台的扫雷,没有想到精简完了代码才200行左右,不过考虑到这是我精简过后的,浓缩才是精华嘛,我就发出来大家一起学习啦,看到程序跑起来能玩,感觉还是蛮有成就感的 ...