我终于搞懂了 Event Loop(宏任务 / 微任务)

一、为什么要学这个

  • 我在哪遇到这个问题

    • 写 Vue / React 时,经常看到:

      • nextTick
      • Promise.then
      • setTimeout
    • 但执行顺序总是"感觉对了,但解释不清楚"

  • 真实踩坑场景

    javascript 复制代码
    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 本质

👉 可以理解为:

一个无限循环的"调度器"

它一直在做这件事:

markdown 复制代码
1. 执行一个宏任务
2. 清空所有微任务
3. 更新 UI(浏览器)
4. 进入下一轮

📦 宏任务(Macrotask)

👉 就是"一整轮任务"

常见:

  • script(整段代码)
  • setTimeout
  • setInterval
  • I/O

👉 类比:

一次"完整的工作流程"


⚡ 微任务(Microtask)

👉 插队任务(优先级极高)

常见:

  • Promise.then
  • MutationObserver
  • Vue 的 nextTick(优先用微任务)

👉 类比:

"老板突然插话:这个先做一下"


🧩 关键区别(核心)

类型 何时执行
宏任务 一轮一轮执行
微任务 当前宏任务结束后 立刻清空

👉 关键一句话:

❗每执行完一个宏任务,必须把微任务全部执行完,才进入下一个宏任务


四、运行机制 / 原理(重点)

我们用刚才那段代码来"拆执行过程"👇


五、代码验证(重点🔥)

javascript 复制代码
console.log(1)

setTimeout(() => {
  console.log(2)
})

Promise.resolve().then(() => {
  console.log(3)
})

console.log(4)

🧠 执行过程(逐步推导)


🟢 第一步:执行主线程(宏任务 #1)

arduino 复制代码
console.log(1) // 输出 1
scss 复制代码
setTimeout(...) // 放入宏任务队列
javascript 复制代码
Promise.then(...) // 放入微任务队列
arduino 复制代码
console.log(4) // 输出 4

👉 此时:

  • 宏任务队列:setTimeout
  • 微任务队列:Promise.then

🟡 第二步:清空微任务队列

arduino 复制代码
console.log(3)

👉 输出:

复制代码
3

🔵 第三步:进入下一轮 Event Loop

执行宏任务:

arduino 复制代码
setTimeout -> console.log(2)

👉 输出:

复制代码
2

✅ 最终结果

复制代码
1
4
3
2

六、总结规律

👉 给你一套"可复用判断模型"(非常关键)


🧠 判断顺序口诀

markdown 复制代码
1. 先执行同步代码(主线程)
2. 遇到宏任务 → 丢到宏任务队列
3. 遇到微任务 → 丢到微任务队列
4. 当前宏任务执行完
5. 立刻清空所有微任务
6. 再执行下一个宏任务

🎯 一句话总结

❗微任务永远在"当前宏任务结束后、下一个宏任务开始前"执行


🧩 判断技巧(工程实用)

看到代码:

👉 先标记:

  • 同步
  • 微任务
  • 宏任务

👉 再按这个顺序排:

复制代码
同步 → 微任务 → 宏任务

七、常见误区


❌ 误区 1:微任务 = 更快执行

👉 错!

微任务不是"更快",而是"更早"


❌ 误区 2:setTimeout 是"立即执行"

scss 复制代码
setTimeout(fn, 0)

👉 实际:

❗只是"尽快进入下一轮"


❌ 误区 3:多个 Promise 是并行执行

javascript 复制代码
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 为什么一定要用微任务》

这个会直接把你带到"框架设计层"。

相关推荐
Dovis(誓平步青云)2 分钟前
浇水提醒刚弹出又消失,植物状态别只存一个百分比
开发语言·前端·javascript·pdf·ecmascript·电脑
码艺-Alimjan34 分钟前
Vben Admin 新增维吾尔语 Vben-Modal的关键坑之一
前端·javascript·vue.js
可乐鸡翅yeah_1 小时前
hls.js 手动自定义 http 请求 loader,修改请求头实战
开发语言·前端·javascript·网络协议·http·ecmascript·m3u8在线
IT_陈寒1 小时前
Vite静态资源导入这个坑我帮你们踩过了
前端·人工智能·后端
广州华水科技2 小时前
大坝安全监测解决方案:单北斗GNSS形变监测系统应用与维护
前端
yivifu2 小时前
中文古籍电子书注释集成
前端·javascript·python·beautifulsoup·epub
默_笙3 小时前
🛴 从散件到整机:DeepAgents 与 Agent 身上预留的那些"插槽"(前置介绍)
前端·javascript
zhangzeyuaaa3 小时前
深入理解 Ruby 运算符:本质、分类、坑点与重载实战
开发语言·前端·ruby
一木 之林3 小时前
DeepSeek Agent 开发(一)
开发语言·前端·javascript
树下水月4 小时前
Typora破解
linux·服务器·前端