宏任务与微任务

在 JavaScript 的事件循环机制中,宏任务(MacroTask)微任务(MicroTask) 是两种不同优先级的异步任务类型,它们的执行顺序直接影响代码的运行逻辑。以下是它们的核心区别和关键细节:


一、核心区别

特性 宏任务(MacroTask) 微任务(MicroTask)
定义 由宿主环境(如浏览器、Node.js)发起的任务 由 JavaScript 自身发起的任务
执行顺序 在事件循环的每一轮中执行一个宏任务 在当前宏任务执行后、下一个宏任务前全部执行
常见示例 setTimeout, setInterval, I/O 操作, UI 渲染 Promise.then/catch/finally, process.nextTick(Node.js), MutationObserver
优先级

二、事件循环流程

  1. 执行一个宏任务(如主代码块、setTimeout回调)。
  2. 执行所有微任务:清空微任务队列。
  3. 渲染更新(浏览器环境下可能触发 UI 渲染)。
  4. 进入下一轮事件循环,执行下一个宏任务。

三、代码示例分析

console.log("Script start"); // 宏任务(主线程)

setTimeout(() => {
  console.log("setTimeout"); // 宏任务(下一轮事件循环)
}, 0);

Promise.resolve()
  .then(() => {
    console.log("Promise 1"); // 微任务
  })
  .then(() => {
    console.log("Promise 2"); // 微任务
  });

console.log("Script end"); // 宏任务(主线程)

输出顺序

Script start → Script end → Promise 1 → Promise 2 → setTimeout

四、关键细节

  1. 微任务优先级更高
    微任务会在当前宏任务结束后立即执行,而宏任务需等待下一轮事件循环。
    例如:在同一个事件循环中,即使宏任务(如 setTimeout)的回调已就绪,也必须等待所有微任务执行完毕。

  2. 微任务会“插队”
    如果在微任务中触发新的微任务,这些新微任务会继续加入当前队列,直到队列清空。这可能导致宏任务被长时间阻塞。

  3. 应用场景

    • 宏任务:适合不紧急的任务(如延迟操作、批量渲染)。
    • 微任务:适合需要高优先级响应的任务(如 Promise 链式调用、DOM 更新后的回调)。

五、常见问题

为什么 PromisesetTimeout 先执行?

  • Promise 回调属于微任务,会在当前宏任务结束后立即执行。
  • setTimeout 是宏任务,必须等待所有微任务执行完毕,再进入下一轮事件循环。

如何避免微任务阻塞?

避免在微任务中编写耗时逻辑,否则会延迟后续宏任务执行,导致页面卡顿。


六、总结

  • 宏任务是 JavaScript 异步的“大颗粒”任务,按事件循环的轮次执行。
  • 微任务是“细颗粒”任务,在当前宏任务结束后立即执行,确保高优先级逻辑及时处理。
  • 理解两者的执行顺序,是避免异步代码 Bug 的关键(如竞态条件、渲染顺序问题)。
posted @ 2025-01-29 17:28  cheeliu  阅读(446)  评论(0)    收藏  举报