Event Loop(事件循环)
Event Loop 即事件循环,是指浏览器或 Node 的一种解决 javaScript 单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。
之所以称之为事件循环,是因为它经常按照类似如下的方式来被实现:
while (queue.waitForMessage()) {
queue.processNextMessage();
}
如果当前没有任何消息,queue.waitForMessage() 会同步地等待消息到达. 每一个消息完整地执行后,其它消息才会被执行。 这为程序的分析提供了一些优秀的特性,包括:一个函数执行时,它永远不会被抢占,并且在其他代码运行之前完全运行(且可以修改此函数操作的数据
问:为什么 JavaScript 是单线程的?
答:JavaScript 的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript 的主要用途是与用户互动,以及操作 DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定 JavaScript 同时有两个线程,一个线程在某个 DOM 节点上添加内容,另一个线程删除这个节点,这时如果删除节点的操作在添加内容之前完成,后者将会报错。
浏览器中的 Event Loop
▶️ 在 JavaScript 中, 任务被分为两种:
一种宏任务( MacroTask) 也叫 Task,定义:当前调用栈中执行的代码成为宏任务。(主代码块,定时器等等);
一种叫微任务( MicroTask)。定义:当前(此次事件循环中)宏任务执行完,在下一个宏任务开始之前需要执行的任务,可以理解为回调事件。(promise.then())
MacroTask(宏任务)- tasks
包括:script 全部代码、 setTimeout、 setInterval)、 I / O、 UI Rendering。
MicroTask(微任务)- jobs
包括: Promise、 MutationObserver
微任务的执行优先级比宏任务高,当微任务全部执行完毕之后(微任务队列为空),才执行宏任务。
Javascript 有一个 main thread 主线程和 call-stack 执行栈,所有的任务都会被放到执行栈等待主线程执行。
- 所有同步任务都在主线程上执行,形成一个执行栈 (Execution Context Stack)。
- 而异步任务会被放置到 Event Table, 当异步任务有了运行结果, 就将该函数移入任务队列(Event Queue)。
- 一旦执行栈中的所有同步任务执行完毕,引擎就会读取任务队列,然后将任务队列中的第一个任务压入执行栈中运行。
- 主线程不断重复第三步,也就是"只要主线程空了,就会去读取任务队列"。该过程不断重复,这就是所谓的 事件循环。
典型题
例一
setTimeout(() => {
console.