【JS档案揭秘】第二集 Event loop与执行栈
我时常在思考关于JS的很多知识在工作中有什么用?是否只能存在于面试这种理论性的东西中,对于我们的业务和工作,它们又能扮演怎样的角色。以后在JS档案揭秘的每一期里,都会加入我对于业务的思考,让这些知识不再是空中楼阁,而是有实际操作的意义。
业务场景
所有的核心在于执行顺序,它能帮助我们正确判断代码按照怎样的顺序去执行。避免因为执行顺序与预期不一致而导致的bug。如果遇到这种因为执行顺序问题而产生的bug,也能通过event loop分析出正确的执行顺序,定位bug并解决。
关键词解释
执行栈:一个专门用来存放执行代码的栈内存结构,它就像一个容器,也叫做执行的主线程。如果遇到函数,会根据回调关系把回调层级深的函数先推入到执行栈底部执行,其他语句依次推入,当全部执行完了后,会从上到下依次弹出执行代码(也就是“先进后出”),供下一次使用;
宏任务(只针对浏览器环境):宿主环境提供的任务,包括:script代码块,MessageChannel,requestAnimationFrame,setTimeout,setInterval;
微任务(只针对浏览器环境):JS自带的任务,包括promise的then和catch,MutationObserver;
任务队列:由宏任务队列与微任务队列组成;
event loop:又名事件循环,它会不断地去微任务队列里取任务放到执行栈执行,当微任务队列被清空时,才去宏任务队列取任务执行。假如这个宏任务里面碰到了微任务和宏任务,会分别推入到任务队列中,并按照“只有微任务队列清空才执行宏任务”的原则来执行代码;
画图理解
过程叙述
当我们说“浏览器是 JS 的家”时,这句话真正的意思是浏览器提供运行时环境来执行我们的JS代码。
浏览器的主要组件包括JS引擎,事件循环,任务队列和Web APIs。上图的任务队列有一点瑕疵,任务队列应该有两个(宏任务队列与微任务队列)。
JS引擎从堆中取出JS代码并及进行分析。假设是一段或多段script代码,它会被认为是一个或几个宏任务,并推入到web apis中。web apis看到是script代码,就把它推入到宏任务队列中,event loop从宏任务队列取到这一段JS代码,并放入执行栈中执行。
在执行当前宏任务时,每当它遇到一些异步代码,如setTimeout
,它又会把它推入到web apis中去执行。当异步代码在Web APIs被执行完后,其回调callback
就被送往任务队列。
event loop不断地监视任务队列(Task Queue),并按它们排队的顺序一次处理一个回调。每当调用栈为空,也就是同步代码执行完毕时,event loop会不断地去微任务队列里取任务放到执行栈执行,当微任务队列被清空时,才去宏任务队列取任务执行。请记住,如果调用栈不是空的,则事件循环不会将任何回调推入执行栈。
例题分析
按照我们之前的理论,首先这里分成两个宏任务,script1和script2。
script1的同步代码先执行,打印111,333。微任务队列推入222,宏任务队列推入444。
此时微任务队列只有222,而宏任务队列为“script2 --- 444”。
所以先清空微任务队列,打印出222,再从宏任务队列中取出script2,推到执行栈中执行。
script2的同步代码先执行,打印555,777。并将666推入微任务队列,888推入宏任务队列。
此时微任务队列只有666,而宏任务队列为“444 --- 888”。
所以先清空微任务队列,打印出666,再从宏任务队列依次取出444和888,并推到执行栈中执行。
综上,打印结果如下:
【JS档案揭秘】第二集 Event loop与执行栈的更多相关文章
- Node.js的事件轮询Event Loop原理
Node.js的事件轮询Event Loop原理解释 事件轮询主要是针对事件队列进行轮询,事件生产者将事件排队放入队列中,队列另外一端有一个线程称为事件消费者会不断查询队列中是否有事件,如果有事件,就 ...
- js事件循环机制 (Event Loop)
一.JavaScript是单线程单并发语言 什么是单线程 主程序只有一个线程,即同一时间片断内其只能执行单个任务. 为什么选择单线程? JavaScript的主要用途是与用户互动,以及操作DOM.这决 ...
- js的事件循环(Event Loop)
(本文从掘金小册整理) 首先介绍一下几个概念 进程与线程 相信大家经常会听到 JS 是单线程执行的,但是你是否疑惑过什么是线程? 讲到线程,那么肯定也得说一下进程.本质上来说,两个名词都是 CPU 工 ...
- 对Node.JS的事件轮询(Event Loop)的理解
title: Node.JS的事件轮询(event loop)的理解 categories: 理解 tags: Node JS 机制 当我们知道I/O操作和创建新线程的开销是巨大的! 网站延迟的开销 ...
- 【JS档案揭秘】第一集 内存泄漏与垃圾回收
程序的运行需要内存,对于一些需要持续运行很久的程序,尤其是服务器进程,如果不及时释放掉不再需要的内存,就会导致内存堆中的占用持续走高,最终可能导致程序崩溃. 不再需要使用的内存,却一直占用着空间,得不 ...
- 【JS档案揭秘】第四集 关于this的讨论到此为止
网上关于this的指向问题的博客文章很多,但大多数都是复制粘贴,也不能用简洁的语言讲清楚,而是不停地写一些示例,看得人云里雾里. 这一集,我只给出结论,以及判定的通用方法,至于是否确实如我所讲,大家可 ...
- 【JS档案揭秘】第三集 深入最底层探秘原型链
关于这部分我看过大量的文章,数不胜数,包括阮一峰的继承三部曲,还有各种慕课的视频教程,网上无数继承方法的对比.也对很多概念存在长期错误的理解.今天做一个正确的总结,用来给原型链和继承这块知识画上句号, ...
- js 在浏览器中的event loop事件队列
目录 前言 认识一个栈两个队列 执行过程 异步任务怎么分配 简单例子 难一点的例子 前言 以下内容是js在浏览器中的事件队列执行,与在nodejs中有所区别,请注意. 都说js是单线程的,不过它本身其 ...
- Js 运行机制和Event Loop
一.为什么JavaScript是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊. Java ...
随机推荐
- 别混淆了sizeof(数组名)和sizeof(指针)
我们在挨个儿输出一个数组中的元素时,最常用的就是用一个for循环来实现,简单了事.比如类似下面的代码片段: for(i = 0; i< length; i++) { printf("数 ...
- 小代学Spring Boot之集成MyBatis
想要获取更多文章可以访问我的博客 - 代码无止境. 上一篇小代同学在Spring Boot项目中配置了数据源,但是通常来讲我们访问数据库都会通过一个ORM框架,很少会直接使用JDBC来执行数据库操作的 ...
- Neo4j电影关系图
“电影关系图”实例将电影.电影导演.演员之间的复杂网状关系作为蓝本,使用Neo4j创建三者关系的图结构,虽然实例数据规模小但五脏俱全. 步骤: 一. 创建图数据:将电影.导演.演员等图数据导入Neo4 ...
- Tomcat CGIServlet enableCmdLineArguments远程代码执行_CVE-2019-0232漏洞复现
Tomcat CGIServlet enableCmdLineArguments远程代码执行_CVE-2019-0232漏洞复现 一.漏洞描述 该漏洞是由于tomcat CGI将命令行参数传递给Win ...
- LiteIDE TARGETARGS -conf_path=E:/PokerServer/src/GameServer/conf/texas.xml -log_dir=E:/PokerServer/src/GameServer/main/log
LiteIDE TARGETARGS -conf_path=E:/PokerServer/src/GameServer/conf/texas.xml -log_dir=E:/PokerServer/s ...
- 算法学习笔记,几个简单的Demo
算法初学的一些心得 前言:现在工作也快一年多了,有时间下班回家会学学算法,陆陆续续也接触了一些 貌似我知道的就冒泡排序其他的都不是很了解 最近买了一本书,边学边记录吧! 一些常用的方法 暴力破解 下面 ...
- [leetcode] 679. 24 Game (Hard)
24点游戏,游戏规则就是利用().+.-. *. /,对四个数字任意运算,可以得出24点则为true. 排列组合问题,最多有A42*A32*A22*4*4*4,也就是12*6*2*4*4=9216种组 ...
- java和golang通过protobuf协议相互通信
目录 整体结构说明 protobuf2文件 golang客户端 目录结构 生成pb.go文件 main.go util.go java服务端 目录结构 pom.xml application.yml ...
- 机器学习-特征值,svd分解
求矩阵的秩 设 ,已知r(A)=2,则参数x,y分别是 解:任意三阶子式=0,有二阶子式≠0,但是这些子式比较多,可以使用初等变换,因为初等变换不改变矩阵的秩,可以将矩阵通过初等行(列)变换,化为行阶 ...
- 十一、SQL Server CONVERT() 函数
定义和用法 CONVERT() 函数是把日期转换为新数据类型的通用函数. CONVERT() 函数可以用不同的格式显示日期/时间数据. 语法 CONVERT(data_type(length),dat ...