乐趣区

简洁明了探索浏览器Event-loop

前段时间我对于浏览器 Event loop 中的 MacroTask 和 MicroTask 哪个先执行有所困惑,苦于搜索也没有发现很明确的答案,于是决定深入探索浏览器 Event loop,现有所愚见,想与大家分享,希望能帮助到那些还在爬坑的人。
1. 什么是 Event loop?

developer.mozilla.org 给出的解释是这样的:

一个 JavaScript 运行时包含了一个待处理的消息队列。每一个消息都关联着一个用以处理这个消息的函数。

在事件循环期间的某个时刻,运行时从最先进入队列的消息开始处理队列中的消息。

大致可以理解为 Event loop 用来处理 JavaScript 事件执行的先后顺序。浏览器端 Event loop 中的异步队列有两种:MacroTask 队列和 MicroTask 队列。它们分别包括:

2. 关于 MacroTask 和 MicroTask。

MicroTask:process.nextTick,promise,MutationObserver,其中 process.nextTick 为 Node 独有。MacroTask:script(整体代码),setTimeout,setInterval,setImmediate,I/O,UI rendering。

浏览器会不断从 task 队列中按顺序取 task 执行。
大体情况如下:

每执行完一个 Macrotask 都会检查 microtask 队列是否为空,如果不为空则会一次性执行完所有 microtask。

3. 同步事件和异步事件怎么处理?

一开始执行栈空,micro 队列空,macro 队列里有且只有一个 script 脚本(整体代码)。然后全局上下文(script 标签)被推入执行栈,同步代码执行。在执行的过程中,会判断是同步任务还是异步任务,通过对一些接口的调用,可以产生新的 macro-task 与 micro-task,它们会分别被推入各自的任务队列里。同步代码执行完了,script 脚本会被移出 macro 队列,这个过程本质上是队列的 macro-task 的执行和出队的过程。需要注意的是:当 macro-task 出队时,任务是一个一个执行的;而 micro-task 出队时,任务是一队一队执行的。因此,我们处理 micro 队列这一步,会逐个执行队列中的任务并把它出队,直到队列被清空。

也就是说循环是这样一个过程:
先执行宏任务, 然后查看是否有微任务队列。如果有,先执行微任务队列中的所有任务,如果没有,会读取宏任务队列中排在最前的任务,执行宏任务的过程中,遇到微任务,依次加入微任务队列。栈空后,再次读取微任务队列里的任务。

4:总结
一句话:

对于浏览器 Event loop 来说,由于 script(整体代码)先执行,所以说 MacroTask 先于 MicroTask 执行。

参考文章:

浏览器与 Node 的事件循环 (Event Loop) 有何区别?
一篇文章教会你 Event loop
什么是浏览器的事件循环

退出移动版