深入理解JavaScript事件循环机制:宏任务与微任务的执行顺序
本文深入解析JavaScript单线程模型下的事件循环机制,详细区分宏任务(如setTimeout)与微任务(如Promise、process.nextTick)的执行优先级。通过具体代码案例,阐明同步代码、微任务队列与宏任务队列在每一轮循环中的执行顺序,帮助开发者准确掌握异步编程的执行逻辑。
JavaScript采用单线程模型且为非阻塞模式。若采用多线程,当不同线程同时对同一DOM节点进行添加或删除操作时,浏览器难以确定以哪个线程的结果为准。为降低复杂性,JavaScript自诞生起便设计为单线程运行。
JavaScript的事件循环机制包含主线程(负责同步代码)和任务队列(处理异步代码)。任务队列进一步细分为宏任务队列和微任务队列。

1. 宏任务(macro-task)包括:
script(整体代码块)、setTimeout、setInterval、setImmediate。
在Node.js环境中,宏任务的执行优先级为:setImmediate > setTimeout / setInterval。
2. 微任务(micro-task)包括:
Promise、MutationObserver(用于观察DOM树结构变化并做出相应处理)、process.nextTick(Node.js环境特有)。
微任务的执行优先级为:process.nextTick > Promise > MutationObserver。
原理概述
执行顺序遵循:同步代码 > 微任务 > 宏任务。
1. 宏任务和微任务均属于任务队列范畴。当一段代码开始执行时,首先执行宏任务中的同步代码部分。
2. 在第一次事件循环中,整个JavaScript脚本被视为一个宏任务进行运行。
3. 若执行过程中遇到setTimeout等宏任务,则将该宏任务内部的回调函数推入宏任务队列,待下一轮宏任务执行时调用。
4. 若执行过程中遇到Promise.then()等微任务,则将其推入微任务队列。在当前宏任务的同步代码执行完毕后,立即依次执行所有微任务。
5. 当本轮循环中所有的同步脚本及微任务队列中的事件执行完毕后,该轮循环结束,随即开始第二轮循环,如此往复。

案例解析
<script>
setTimeout(() => {
console.log('4')
}, 100)
new Promise((resolve) => {
console.log('1')
resolve("成功")
}).then(data => {
console.log(data)
})
console.log('2')
</script>具体分析如下:
1. 该段代码作为宏任务进入主线程执行。
2. 首先遇到setTimeout等宏任务,将其内部函数推入宏任务队列,等待下一轮宏任务执行时调用。
3. 接着遇到Promise,其同步代码部分立即执行,而回调函数被分发至微任务队列。
4. 执行console.log('2')并立即输出,随后检查是否存在微任务。由于存在then的回调函数,立即执行该微任务。
5. 第一轮宏任务执行完毕。
6. 开始第二轮宏任务,执行宏任务队列中的setTimeout函数。
对上述代码进行升级测试:
setTimeout(() => {
console.log('1')
}, 1000)
new Promise((resolve) => {
console.log('2')
setTimeout((params) => {
resolve("4")
console.log("3");
}, 100)
}).then(data => {
console.log(data)
})
console.log('5') 能否先将promise.then分发到微任务中?
答案是否定的。因为只有当Promise状态变为resolved(成功回调)后,才能执行.then。在当前场景下,resolve位于setTimeout宏任务中,必须等待setTimeout执行完毕后,才能触发.then中的微任务。
具体解析如下:
1. 该段代码作为宏任务进入主线程。
2. 首先遇到等待时间为1000ms的setTimeout,记为s1,并将其内部函数推入宏任务队列,等待下一轮执行。
3. 接着遇到Promise,其同步代码立即执行。内部包含一个等待时间为100ms的setTimeout,记为s2,且Promise的resolve操作也在此处,因此s2也被放入下一轮宏任务队列。
4. 执行console.log('5')并立即输出。
5. 第一轮宏任务执行结束。检查当前是否有可执行的微任务,若无则执行宏任务。
6. 开启第二轮宏任务,发现队列中有两个宏任务。根据等待时间长短比较,时间短的先执行。先执行s2,发现其中包含微任务promise.then,立即执行该微任务,随后再执行s1。
最终的输出顺序为:2 5 3 4 1。
以上为个人经验分享,希望能为大家提供参考,也希望大家多多支持本站。
JavaScript入门书籍避坑:这些书不适合初学者
2026-11-05 16:31:15
JS使用Math.round实现保留两位小数的原理与代码
2026-11-05 15:35:55
JavaScript 原生 Date 对象怎么实现日期加减
2026-11-05 14:30:00
JavaScript函数定义的三种方式:声明、表达式与箭头函数
2026-11-05 13:50:55
JavaScript中escape、encodeURI和encodeURIComponent对空格的处理差异
2026-11-05 12:47:23
