之前的文章中,有一篇是提到了Vue.$nextTick(使用vue nextTick解决的一个问题),后面想着去学习相关的知识,发现js中的Event Loop(事件循环)还是十分重要且需要理解的内容,虽然以前也有了解过,但毕竟不够深入,本文就是总结一些关于Event Loop比较重要的知识
相关概念
以下的概念是理解Event Loop所必须的,相信大家在学习js的过程中,也或多或少的遇到过。
同步事件、异步事件
同步事件指的就是js中按照从上到下的顺序依次、先后执行的事件;对于大多数的js语句来说都是同步的,一条语句执行完了,就执行下一条语句。
异步事件则通常是一些比较消耗时间、需要执行一系列操作的事件,最常见的异步事件就是api请求;对于异步事件,通常我们只需要得到它的结果就好了,所以在执行的过程中它会被挂起,待到有结果时再执行后续操作。
执行栈(call-stack 也叫调用栈)
首先理解一下js的内存模式,在js中,数据结构主要存在栈(stack)和堆(heap),其中堆主要用以存储复杂数据类型的变量;而执行栈主要是用来存储和执行上下文,而由于基本数据类型的变量值都被保存在执行上下文中,所以基本数据类型也是存储在执行栈中(闭包除外),除此之外,复杂数据类型的引用也是如此
区别 | 栈 | 堆 |
---|---|---|
数据结构 | 栈结构 | 堆结构 |
存储内容 | 基本数据类型和复杂数据类型的引用、执行上下文 | 复杂数据类型和闭包 |
空间 | 小 | 大 |
任务队列
在js中,有两种任务队列,分别是宏任务队列和微任务队列
宏任务队列:script全部代码(可以把整个script当成一个宏任务)、setTimeOut、setInterval、I/O、UI Rendering
微任务队列:Promise、Process.nextTick(node)
Event Loop的概念
Event Loop是用来协调各种事件、用户交互、脚本执行、UI渲染、网络请求等的一种机制。
在js中使用这种机制是因为js本身是单线程的,如果把所有事件都依次从上到下执行,那么在遇到一些耗时的事件(如请求api),还没有结果时,整个js线程就会在这里干等着,在用户看来,就是程序发生了阻塞。
而使用了Event Loop机制后,就会使得js执行的效率变高,既然有耗时的事件,为何不先放在一旁,先做可以立即完成的,然后等耗时的有结果了再进行下一步操作,而Event Loop就是这么一种事件协调的机制。
Event Loop的过程
在Event Loop中,每一次循环称为一次tick,每一次tick会以下操作:
- 调用栈选择最先进入队列的宏任务(一般是script 整体代码),如果有则执行;
- 检查是否存在微任务,如果存在则不停地执行,直至清空微任务队列;
-
浏览器更新渲染
结合以下的图片理解:
Event Loop.png
可能上图会有所误导,让人以为存在多个微任务队列,而只存在一个宏任务队列,其实不然,每个宏任务队列都保证按照回调函数入队列的顺序依次执行宏任务,在宏任务或者微任务中产生的新微任务 会被压入到微任务队列中并执行。而在执行两个宏任务之间,也即在执行下一个宏任务之前,会优先执行完所有微任务,也即会优先清空已有的微任务队列。所以说,上图中第二个微任务队列产生的时候,第一个微任务队列已被清空,也就是实际上只有一个微任务队列,它们不会同时存在。