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个线程:

  1. JS引擎:负责执行执行栈的最顶部代码
  2. GUI线程:负责渲染页面
  3. 事件监听线程:负责监听各种事件
  4. 计时线程:负责计时(setTimeout、setInterval)
  5. 网络线程:负责网络通信 如 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引擎从事件队列中取出处理程序来执行,以及与宿主环境的配合,称之为事件循环

事件队列在不同的宿主环境中有所差异,大部分宿主环境会将事件队列进行细分。在浏览器中,事件队列被分为两种:

  1. 宏队列:macroTask,计时器结束的回调、事件回调、http回调等等绝大部分异步函数是进入宏队列
  2. 微队列:MutationObserver,Promise产生的回调进入微队列

当执行栈清空是,JS引擎首先会将微任务中的所有任务一次执行结束,如果没有微任务,则执行宏任务。

js——事件循环的更多相关文章

  1. Node.js 事件循环(Event Loop)介绍

    Node.js 事件循环(Event Loop)介绍 JavaScript是一种单线程运行但又绝不会阻塞的语言,其实现非阻塞的关键是“事件循环”和“回调机制”.Node.js在JavaScript的基 ...

  2. Node.js事件循环

    Node JS是单线程应用程序,但它通过事件和回调概念,支持并发. 由于Node JS每一个API是异步的,作为一个单独的线程,它使用异步函数调用,以保持并发性.Node JS使用观察者模式.Node ...

  3. js事件循环机制辨析

     对于新接触js语言的人来说,最令人困惑的大概就是事件循环机制了.最开始这也困惑了我好久,花了我几个月时间通过书本,打代码,查阅资料不停地渐进地理解他.接下来我想要和大家分享一下,虽然可能有些许错误的 ...

  4. 6、Node.js 事件循环

    #########################################################################################Node.js 事件循 ...

  5. Node.js 事件循环

    Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高. Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发. Node.j ...

  6. Node.js 学习(五)Node.js 事件循环

    Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高. Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发. Node.j ...

  7. js事件循环

    之前有看过一些事件循环的博客,不过一阵子没看就发现自己忘光了,所以决定来自己写一个博客总结下! 首先,我们来解释下事件循环是个什么东西: 就我们所知,浏览器的js是单线程的,也就是说,在同一时刻,最多 ...

  8. [译] 所有你需要知道的关于完全理解 Node.js 事件循环及其度量

    原文地址:All you need to know to really understand the Node.js Event Loop and its Metrics 原文作者:Daniel Kh ...

  9. Node.js 事件循环机制

    Node.js 采用事件驱动和异步 I/O 的方式,实现了一个单线程.高并发的 JavaScript 运行时环境,而单线程就意味着同一时间只能做一件事,那么 Node.js 如何通过单线程来实现高并发 ...

随机推荐

  1. ThreeJS系列1_CinematicCameraJS插件详解

    ThreeJS系列1_CinematicCameraJS插件详解 接着上篇 ThreeJS系列1_CinematicCameraJS插件介绍 看属性的来龙去脉 看方法作用 通过调整属性查看效果 总结 ...

  2. JAVA学习线路:day01面向对象(继承、抽象类)

    所有的文档和源代码都开源在GitHub: https://github.com/kun213/DailyCode上了.希望我们可以一起加油,一起学习,一起交流. day01面向对象[继承.抽象类] 今 ...

  3. jquery购物车全选,取消全选,计算总金额

    这是html代码 <div class="gwcxqbj"> <div class="gwcxd center"> <div cl ...

  4. C#实例(经典):四路光电开关&激光雷达数据采集和波形图绘制

    前言:本文全部纯手工打造,如有疏漏之处,还请谅解! 如果需要查看更多文章,请微信搜索公众号 csharp编程大全,需要进C#交流群群请加微信z438679770,备注进群, 我邀请你进群! ! ! 这 ...

  5. kubernetes-介绍与特性

    1. kubernetes概述 1) kubernetes是什么 2) kubernetes能做什么 3) kubernetes特性 4) kubernetes集群架构与组件 5) kubernete ...

  6. 2017-01-26--编译Linux内核2.6.30版本报错解决

    错误一: LD .tmp_vmlinux1 init/built-in.o: In function `run_init_process': /home/ox/tq2440/opt/EmbedSky/ ...

  7. pytest文档48-切换 base_url 测试环境(pytest-base-url)

    前言 当我们自动化代码写完成之后,期望能在不同的环境测试,这时候应该把 base_url 单独拿出来,能通过配置文件和支持命令行参数执行. pytest-base-url 是 pytest 里面提供的 ...

  8. 【并查集】BZOJ 1854 连续攻击游戏

    题目内容 洛谷链接 lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示.当他使用某种装备时,他只能使用该装备的某一个属性.并 ...

  9. MySQL数据库基础-2范式

    数据库结构设计 范式 设计数据库的规范 第12345范式,凡是之间有依赖关系. 关系模型的发明者埃德加·科德最早提出这一概念,并于1970 年代初定义了第一范式.第二范式和第三范式的概念 设计关系数据 ...

  10. Storage API简介和存储限制与逐出策略

    目录 简介 常用的客户端存储方式 data storage的类型 逐出策略 Storage API estimate persist persisted 综合使用 总结 简介 对于现代浏览器来说,为了 ...