Skip to content

深入浅出 JavaScript 事件循环:一次彻底搞懂宏任务与微任务

“JavaScript 是单线程的,但它为什么可以处理异步任务?”
“为什么 Promise 比 setTimeout 先执行?”
“微任务到底有多微?”

这些问题,绕不开一个关键词:事件循环(Event Loop)。今天我们就用通俗的语言,讲清楚 JS 异步背后的“神秘机制”。

什么是事件循环?

事件循环是 JavaScript 用来处理同步和异步代码执行顺序的一种机制,它确保:

同步代码立即执行,异步代码在合适的时机执行。

更正式地说:

事件循环是 JavaScript 执行环境中调度任务执行的核心机制。它负责从任务队列中依次取出任务并执行,确保单线程的 JavaScript 能够以非阻塞的方式处理异步操作。

为什么需要事件循环?

JavaScript 是单线程的 —— 所有代码只能一个接一个执行。但在实际应用中我们经常要做:

  • 网络请求
  • 读取文件
  • 定时器
  • 等待用户点击

这些事如果“卡”在主线程里,整个页面就“假死”了。于是,事件循环机制就登场了:它允许这些操作“异步地”执行,并在结果准备好后重新排入任务队列。

一、JavaScript 是单线程的

JavaScript 设计之初就是运行在浏览器中的“脚本语言”,初衷就是操作网页。网页 UI 渲染也只能有一个线程,如果 JS 能多线程同时改 DOM,那页面得乱套。

于是,JavaScript 是单线程的。

但现代 Web 不可能没有异步:网络请求、定时器、点击事件……那 JS 是怎么做到“又单线程又异步”的呢?

这就要靠主角:事件循环(Event Loop)

二、事件循环是怎么工作的?

简单来说,事件循环就像一个无限循环的工厂,它在不停地执行任务。但它有严格的流程:

  1. 执行一个宏任务(比如主线程代码或 setTimeout
  2. 执行所有微任务(比如 Promise.then
  3. 浏览器进行UI 渲染
  4. 回到第 1 步

注意:每个宏任务执行完之后,都会清空所有的微任务!


三、宏任务 和 微任务 到底是什么?

✅ 宏任务(Macro Task)

由浏览器发起的“大的任务块”,比如:

  • 整体脚本(主线程代码)
  • setTimeout
  • setInterval
  • setImmediate(Node 专属)
  • requestAnimationFrame

宏任务之间执行是“分开的”,一个执行完,才轮到下一个。


✅ 微任务(Micro Task)

由 JavaScript 自己产生的“更细粒度的异步任务”,比如:

  • Promise.then / catch / finally
  • queueMicrotask
  • MutationObserver

微任务会在当前宏任务结束后、下一个宏任务开始前全部执行完。


四、实际运行顺序示例

js
console.log('1');

setTimeout(() => {
  console.log('2');
}, 0);

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

console.log('4');