[4-1] 异步编程与事件循环 · 事件循环底层机制 (Event Loop & Task Queues)

3 阅读2分钟

所属板块:4. 异步编程与事件循环

记录日期:2026-03-xx
更新:遇到输出题或宏/微任务相关题时补充

1. JS 单线程与异步的本质

JavaScript 本身是单线程的(只有一个主调用栈 Call Stack)。
所有同步代码必须排队执行,但前端需要处理网络请求、定时器、DOM 事件等耗时操作——这些操作不能阻塞主线程。

解决方案:JS 引擎把耗时任务交给宿主环境(浏览器/Node.js),宿主环境在后台执行完后再把回调“通知”回来。这就是事件循环(Event Loop)的由来。

记住:JS 引擎只负责执行代码,异步调度由宿主环境 + Event Loop 共同完成

2. 浏览器端的事件循环模型(核心运转流程)

整个流程可以拆成以下几个部分:

  • 执行栈 (Call Stack):执行同步代码(包括 new Promise 的 executor)
  • Web APIs:浏览器提供的异步线程(定时器、HTTP 请求、DOM 事件等)
  • 任务队列
    • 宏任务队列(Macrotask Queue)
    • 微任务队列(Microtask Queue)

Event Loop 的完整运转顺序(每次循环叫一个 Tick):

  1. 执行栈为空
  2. 先清空微任务队列(全部执行完)
  3. 执行一个宏任务
  4. 再次清空微任务队列
  5. (可选)渲染页面(UI 更新)
  6. 重复以上步骤

关键规则:一个宏任务之后,必须把当前所有微任务全部执行完,才能执行下一个宏任务。

3. 宏任务 vs 微任务(高频考点)

宏任务(Macrotask)

  • script(整体代码)
  • setTimeout / setInterval
  • setImmediate(Node)
  • I/O 操作
  • UI 渲染

微任务(Microtask)

  • Promise.then / catch / finally
  • MutationObserver
  • process.nextTick(Node,优先级极高)

经典执行顺序示例:

console.log("1");

setTimeout(() => console.log("2"), 0);

Promise.resolve().then(() => console.log("3"));

console.log("4");

输出顺序:1 → 4 → 3 → 2
原因:同步 → 微任务(Promise) → 宏任务(setTimeout)

4. Node.js 事件循环的差异(进阶了解)

Node.js 事件循环分为 6 个阶段(Timers → I/O callbacks → Idle/prepare → Poll → Check → Close callbacks)。

重要区别:

  • Node 11 之前:微任务在每个阶段结束后执行
  • Node 11 之后:与浏览器更接近(每个宏任务后清空微任务)
  • process.nextTick 优先级高于所有微任务(几乎最先执行)

5. 小结 & 复习时的“调度视角”

  • 遇到任何输出题,先画出:
    • 执行栈 → 宏任务队列 → 微任务队列
    • 严格遵守“一个宏任务后清空所有微任务”
  • [4-1] 的事件循环机制是整个异步板块的底层基础,后面的 Promise、async/await 都会放到这个模型里执行

下一篇文章会进入 [4-2]:异步编程方案的演进(从回调地狱到 Promise 的状态流转与核心 API)。

返回总目录:戳这里