我终于搞懂了 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 为什么一定要用微任务》

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

相关推荐
用户806138166592 小时前
避免滥用“事件总线”
前端
@大迁世界2 小时前
13.在 React 中应怎样正确更新 state?
前端·javascript·react.js·前端框架·ecmascript
终端鹿2 小时前
Suspense 异步组件与懒加载实战
前端·vue.js
清风细雨_林木木2 小时前
CSS 报错:css-semicolonexpected 解决方案
前端·css
Jinuss2 小时前
源码分析之React中useRef解析
前端·javascript·react.js
cch89182 小时前
css 样式说明,在页面布局开发中,样式表用于控制组件的尺寸、间距、边框及背景等视觉表现
前端·javascript·html
被AI抢饭碗的人2 小时前
QT:基础与信号槽
前端·qt
熙街丶一人2 小时前
css 图片未加载时默认高度,加载后随图片高度
前端·javascript·css
xiaoliuliu123452 小时前
Android Studio 2025 安装教程:详细步骤+自定义安装路径+SDK配置(附桌面快捷方式创建)
java·前端·数据库