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. python 报错 wxPyDeprecationWarning: Using deprecated class PySimpleApp.

    如题:python 报错 提示为 : wxPyDeprecationWarning: Using deprecated class PySimpleApp. 解决:将 wx.PySimpleApp() ...

  2. Nuget管理自己的项目库

    Nuget是什么 Nuget 是一种 Visual Studio 扩展工具,它能够简化在 Visual Studio 项目中添加.更新和删除库(部署为程序包)的操作.(官方地址)相信大家对这个应该还是 ...

  3. 058 01 Android 零基础入门 01 Java基础语法 06 Java一维数组 05 案例:求数组元素的最大值

    058 01 Android 零基础入门 01 Java基础语法 06 Java一维数组 05 案例:求数组元素的最大值 本文知识点:求数组元素的最大值 案例:求数组元素的最大值 程序代码及其执行过程 ...

  4. Python3基础——字符串类型

    Text Sequence Type - str(immutable) class str(object='') class str(object=b'', encoding='utf-8', err ...

  5. 【题解】小M的作物

    题目戳我 \(\text{Solution:}\) 这题要求最大收获,可以转化为所有可能的收益减去最小割. 单个点很好连边 \((S\to pos\to T),\) 问题在于如何处理组合的点. 观察到 ...

  6. java高级项目 jdbc与数据库连接数据库

    //图书管类 public class Book { private Integer id; private String b_name; private double b_price; privat ...

  7. 基于Intel x86 Android的RAD游戏开发

    zip文件还包含编译的"MonkeyGame-debug".可以在模拟器中运行的二进制文件.在"game.build"文件夹中有一个HTML5 build.在C ...

  8. visio调整画布大小的简便方法

    按住Ctrl,然后鼠标在边缘拉拽即可.

  9. English 介词

    English 介词 Create Time : 2019-06-27 表示时间的介词称为时间介词.表示时间的介词有:at, on, in, before, after等. 一.at, on和in ① ...

  10. MeteoInfoLab脚本示例:inpolygon

    inpollygon函数是用来判断带坐标(x/y)的数据是否在某个或者一组多边形(Polygon)中,返回的结果中如果做多边形内则值为1,否则值为-1.下面一个例子演示了利用一个shape文件和inp ...