Skip to content

事件循环EventLoop

在JS中,任务分为同步任务和异步任务。同步任务由JS执行引擎执行,形成一个执行栈。异步任务在触发条件达成后,它的回调函数会被放置到任务队列中。当执行栈中所有的同步任务执行完毕时,JS执行引擎就会将任务队列放置到执行栈中执行。

异步任务分为:

  • 宏任务(macrotask):

    • 一般指整段 script 脚本、setTimeout/setInterval 等延迟器、dom 事件的回调事件等
    • 浏览器为了能使宏任务能够有序执行,会在一个宏任务结束后,下一个宏任务执行前,对页面进行重新渲染,流程如下:

    macrotask -> 渲染 -> macrotask -> ...

  • 微任务(microtask)

    • 一般是指promise.then函数、MutationObserver等
    • 可以理解为在当前宏任务执行完毕时,立即执行的任务

事件队列在不同的宿主环境中有所差异,大部分宿主环境会将事件队列进行细分。

浏览器中的事件循环

在浏览器中,事件队列根据异步任务分为了宏任务队列和微任务队列。

浏览器执行宏任务和微任务的顺序如下:

  • 执行宏任务队列中的一个宏任务(即整段 script 代码)

  • 当前宏任务执行过程中产生的宏任务、微任务会被放置到对应队列中

  • 当前宏任务执行完毕时,依次执行微任务队列中所有的微任务

    • 注意!在执行微任务过程中产生的微任务也需要一起清空,才会开始执行下一个宏任务
  • 在下一个宏任务执行前,由 GUI 线程开始渲染

  • 渲染结束后,由JS执行线程接管,开始执行下一个宏任务

流程图如下:

68747470733a2f2f692e6c6f6c692e6e65742f323031392f30322f30382f356335643661353238626461662e6a7067

为什么会网上有人争论宏任务和微任务哪个先执行?

先说结论,宏任务先执行。

因为在第一次执行事件循环的时候,是将整段 script 脚本作为宏任务开始的,认为微任务先执行的人实际上是忽略了第一次事件循环。