一、为什么要学这个
-
我在哪遇到这个问题
-
写 Vue / React 时,经常看到:
nextTickPromise.thensetTimeout
-
但执行顺序总是“感觉对了,但解释不清楚”
-
-
真实踩坑场景
console.log(1) setTimeout(() => { console.log(2) }) Promise.resolve().then(() => { console.log(3) }) console.log(4)
👉 我当时:知道答案是 1 4 3 2,但不知道为什么
-
为什么必须搞懂(工程意义)
- UI 更新为什么“延迟一拍”
- Vue 的
nextTick为什么这么设计 - 为什么有时候
setTimeout会“卡” - 如何避免“状态更新错乱”
👉 本质一句话:
❗Event Loop 决定了你代码的“执行顺序”和“时机控制”
二、我一开始的理解(错误认知)
👉 这是我当时非常真实的理解(现在看是错的)
-
我原来以为:
“JS 是单线程,所以代码就是从上到下执行,遇到异步就丢到后面”
-
我对宏任务 / 微任务的理解:
- 宏任务:大任务(setTimeout)
- 微任务:小任务(Promise)
👉 问题来了:
- ❓ 为什么 Promise 一定比 setTimeout 先执行?
- ❓ “小任务”凭什么优先?
- ❓ DOM 更新为什么在某些时机才发生?
👉 最致命的问题:
❗我不知道“什么时候清空微任务队列”
三、核心概念拆解(用人话讲清楚)
我们不用官方定义,直接用“人话”👇
🧠 Event Loop 本质
👉 可以理解为:
一个无限循环的“调度器”
它一直在做这件事:
1. 执行一个宏任务
2. 清空所有微任务
3. 更新 UI(浏览器)
4. 进入下一轮
📦 宏任务(Macrotask)
👉 就是“一整轮任务”
常见:
script(整段代码)setTimeoutsetInterval- I/O
👉 类比:
一次“完整的工作流程”
⚡ 微任务(Microtask)
👉 插队任务(优先级极高)
常见:
Promise.thenMutationObserver- Vue 的
nextTick(优先用微任务)
👉 类比:
“老板突然插话:这个先做一下”
🧩 关键区别(核心)
| 类型 | 何时执行 |
|---|---|
| 宏任务 | 一轮一轮执行 |
| 微任务 | 当前宏任务结束后 立刻清空 |
👉 关键一句话:
❗每执行完一个宏任务,必须把微任务全部执行完,才进入下一个宏任务
四、运行机制 / 原理(重点)
我们用刚才那段代码来“拆执行过程”👇
五、代码验证(重点🔥)
console.log(1)
setTimeout(() => {
console.log(2)
})
Promise.resolve().then(() => {
console.log(3)
})
console.log(4)
🧠 执行过程(逐步推导)
🟢 第一步:执行主线程(宏任务 #1)
console.log(1) // 输出 1
setTimeout(...) // 放入宏任务队列
Promise.then(...) // 放入微任务队列
console.log(4) // 输出 4
👉 此时:
- 宏任务队列:
setTimeout - 微任务队列:
Promise.then
🟡 第二步:清空微任务队列
console.log(3)
👉 输出:
3
🔵 第三步:进入下一轮 Event Loop
执行宏任务:
setTimeout -> console.log(2)
👉 输出:
2
✅ 最终结果
1
4
3
2
六、总结规律
👉 给你一套“可复用判断模型”(非常关键)
🧠 判断顺序口诀
1. 先执行同步代码(主线程)
2. 遇到宏任务 → 丢到宏任务队列
3. 遇到微任务 → 丢到微任务队列
4. 当前宏任务执行完
5. 立刻清空所有微任务
6. 再执行下一个宏任务
🎯 一句话总结
❗微任务永远在“当前宏任务结束后、下一个宏任务开始前”执行
🧩 判断技巧(工程实用)
看到代码:
👉 先标记:
- 同步
- 微任务
- 宏任务
👉 再按这个顺序排:
同步 → 微任务 → 宏任务
七、常见误区
❌ 误区 1:微任务 = 更快执行
👉 错!
微任务不是“更快”,而是“更早”
❌ 误区 2:setTimeout 是“立即执行”
setTimeout(fn, 0)
👉 实际:
❗只是“尽快进入下一轮”
❌ 误区 3:多个 Promise 是并行执行
Promise.resolve().then(() => console.log(1))
Promise.resolve().then(() => console.log(2))
👉 实际:
❗是按顺序进入微任务队列,一个一个执行
八、我现在的理解
👉 从“模糊”到“清晰”的变化:
❌ 以前
- Promise 比 setTimeout 快(但不知道为什么)
- nextTick 是“异步优化”(但不理解本质)
✅ 现在
👉 我会这样理解:
Event Loop 是一个“宏任务驱动 + 微任务插队”的调度系统
👉 更工程化一点:
- 宏任务:控制节奏(tick)
- 微任务:做“收尾 / 修正 / 合并更新”
👉 这就是为什么:
- Vue 要用
nextTick - React 要做批处理(batch update)
九、扩展方向
如果你已经理解到这里,可以继续深入👇
🚀 1. Vue 的 nextTick
👉 本质:
利用微任务,在 DOM 更新后执行回调
🚀 2. React Scheduler
👉 本质:
控制任务优先级 + 可中断渲染
🚀 3. 浏览器渲染时机
- 微任务之后
- 宏任务之间
🚀 4. Node.js Event Loop(更复杂)
- 不同阶段(timers / poll / check)
最后一刀总结(帮你彻底记住)
❗Event Loop = 宏任务一轮一轮跑 + 每轮结束必须清空微任务
如果你愿意,下一篇我可以帮你写一个“更狠的”👇
👉 《我终于搞懂了 Vue nextTick 为什么一定要用微任务》
这个会直接把你带到“框架设计层”。