一、为什么要有事件循环

  • 在线程运行的时候,需要不断地接受新的任务 => 我们需要一个“轮询”的循环机制
  • 页面运行过程中,有很多不同类型的事件,我们需要按照某种顺序去执行 => “消息队列”诞生

【为了解决各种不同类型的异步操作,让他们以合理的顺序执行】

二、事件循环的过程——时序图

通过上面的描述,我们已经可以分析出:

  • 需要1个执行线程
  • 需要1个队列
  • 需要1个消息通信线程

JS事件循环 - 图1

如何讲述:
主线程执行完当前的同步代码,此后主线程空闲 => 发现消息队列里有异步任务,拿过来执行 => 执行完毕后空闲 => 再次轮询到有异步任务,执行【这个不断查询,获取,执行】的过程就是一个循环,我们称它为“事件循环”。

它的参与者从宏观上主要是上述的进程与线程。

三、事件循环中的宏任务、微任务

先执行宏任务、还是先执行微任务

答:假设一个同步代码,先发起一个 setTimeout,又发起了一个 Promise,它会先执行 Promise => setTimeout。
当然,此时他们其实不在一个 tick 中,setTimeout 是新开了一个 tick。

只能说:同一层级下,微任务永远比宏任务先执行

什么是微任务?

对于宏任务还是比较好理解的,通过JS调用一些API、或者其他进程通过IO线程 => 事件触发线程 => 宏任务队列。

微任务是在当批宏任务里面产生的,每个宏任务执行的时候,会创建自己的微任务队列。
在一个宏任务里,分别创建一个宏任务回调、微任务回调,一定是微任务更早于宏任务执行。

从表象上看,微任务能做什么?插队!。

宏任务
  • JS主线程-script整体代码
  • 定时器线程-setTimetout/setInterval
  • UI交互事件=>Dom事件
  • I/O => socket,文件交互

    微任务
  • Promise

  • MutationObserver
    为什么要区分宏任务、微任务
    答:便于插队。状态的同步,对视图来说尤为重要。如果不区分,则无法再下一次宏任务之前插队。

    四、说点其他

    经常看见网上会把一些执行栈的内容,放在这里。
    从我本人的角度看,貌似这个其实反而会混淆概念。
    【执行栈】和【堆】是JS固有的运行流程。
    哪怕没有事件循环,JS也是按照:全局执行上下文、根据函数顺序创建的函数执行上下文运行。