事件循环EventLoop
在JS中,任务分为同步任务和异步任务。同步任务由JS执行引擎执行,形成一个执行栈。异步任务在触发条件达成后,它的回调函数会被放置到任务队列中。当执行栈中所有的同步任务执行完毕时,JS执行引擎就会将任务队列放置到执行栈中执行。
异步任务分为:
宏任务(macrotask):
- 一般指整段 script 脚本、setTimeout/setInterval 等延迟器、dom 事件的回调事件等
- 浏览器为了能使宏任务能够有序执行,会在一个宏任务结束后,下一个宏任务执行前,对页面进行重新渲染,流程如下:
macrotask -> 渲染 -> macrotask -> ...微任务(microtask)
- 一般是指promise.then函数、MutationObserver等
- 可以理解为在当前宏任务执行完毕时,立即执行的任务
事件队列在不同的宿主环境中有所差异,大部分宿主环境会将事件队列进行细分。
浏览器中的事件循环
在浏览器中,事件队列根据异步任务分为了宏任务队列和微任务队列。
浏览器执行宏任务和微任务的顺序如下:
执行宏任务队列中的一个宏任务(即整段 script 代码)
当前宏任务执行过程中产生的宏任务、微任务会被放置到对应队列中
当前宏任务执行完毕时,依次执行微任务队列中所有的微任务
- 注意!在执行微任务过程中产生的微任务也需要一起清空,才会开始执行下一个宏任务
在下一个宏任务执行前,由 GUI 线程开始渲染
渲染结束后,由JS执行线程接管,开始执行下一个宏任务
流程图如下:

为什么会网上有人争论宏任务和微任务哪个先执行?
先说结论,宏任务先执行。
因为在第一次执行事件循环的时候,是将整段 script 脚本作为宏任务开始的,认为微任务先执行的人实际上是忽略了第一次事件循环。