本文深入解析JavaScript的事件循环(Event Loop)机制,详细阐述同步任务、宏任务(MacroTask)与微任务(MicroTask)的区别与执行顺序。通过剖析Promise、async/await及定时器的行为,揭示主线程、任务队列与微任务队列的交互逻辑,帮助开发者掌握异步代码的执行规律。
首先观察以下代码片段:

这段代码的控制台打印顺序究竟是如何确定的?
正确的输出顺序为:script start, script end, promise1, promise2, setTimeout。
这一结果涉及核心概念:事件循环(Event Loop)、宏任务(MacroTask)以及微任务(MicroTask)。
事件循环 Event Loop
一、事件循环 Event Loop
JavaScript引擎通常维护两个线程:一个是负责执行代码的“主线程”,另一个是负责处理主线程与其他进程(主要是各类I/O操作)通信的“Event Loop线程”,也可称为消息线程。
根据执行方式,所有任务可划分为两类:同步任务(Synchronous)和异步任务(Asynchronous)。
同步任务是指在主线程上按顺序排队执行的任务,必须等待前一个任务完全执行完毕后,才能开始执行下一个任务。
异步任务则不直接进入主线程,而是进入“任务队列”(Task Queue)。只有当任务队列通知主线程该异步任务可以执行时,它才会被推入主线程执行。
通常情况下,异步任务主要包含以下三种类型:
1、普通事件,例如click、resize等交互事件。

2、资源加载事件,例如load、error等。

3、定时器功能,包括setInterval和setTimeout。

事件循环的具体工作流程如下:
- 同步任务直接加入主线程执行,而异步任务则注册到Event Table中。
- 当异步任务执行完成后,Event Table会将对应的回调函数移入Event Queue。
- 当主线程内的任务执行完毕,即执行栈变为空时,系统会去Event Queue中读取对应的函数,并将其推入主线程执行。
- 上述过程会不断重复,这就是所谓的Event Loop(事件循环)。
宏任务与微任务
二、宏任务与微任务
在JavaScript中,任务被进一步细分为两种:宏任务(MacroTask)和微任务(MicroTask)。
2.1 MacroTask(宏任务)
宏任务由宿主环境(如浏览器和Node.js)提供。
常见的宏任务包括:整个script全部代码、setTimeout、setInterval。
为了确保JS内部任务与DOM渲染有序执行,浏览器会在每个task执行结束后、下一个task开始前,对页面进行重新渲染(即task -> 渲染 -> task...的模式)。
2.2 MicroTask(微任务)
微任务由语言标准定义。
常见的微任务包括:Promise、await。
async函数表示函数内部可能包含异步方法。当async方法执行时,遇到await会立即执行其后的表达式,并将await表达式后面的代码放入微任务队列,从而让出执行栈给同步代码先执行。
async function foo() {
var a = await new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 2000);
});
console.log(a); // 第2秒时输出: 1
}
foo();
2.3 宏任务与微任务的执行顺序:
- 当执行栈执行完所有同步任务后,系统会检查执行栈是否为空。如果为空,则进一步检查微任务队列是否为空。若微任务队列为空,则执行宏任务;否则,将一次性执行完所有微任务。
- 每次单个宏任务执行完毕后,系统会检查微任务队列是否为空。如果不为空,则按照先入先出的规则全部执行完微任务后,将微任务队列置为null,然后再执行下一个宏任务,如此循环。
总结:执行顺序遵循 同步 -> 微任务 -> 宏任务 的规则。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。