eventLoop宏任务与微任务详解

宏任务(Macrotask)与微任务(Microtask)详解

在 JavaScript 的事件循环(Event Loop)机制中,任务分为两大类:宏任务微任务。理解它们的区别是掌握异步编程的关键。


一、宏任务(Macrotask/Task)

宏任务是事件循环的基本单位,每次事件循环只执行一个宏任务(从队列中取出第一个)。执行完宏任务后,会清空所有微任务队列。

常见宏任务:

  1. setTimeout() - 定时器回调
  2. setInterval() - 间隔定时器回调
  3. setImmediate() - Node.js 特有(比 setTimeout(fn, 0) 优先级高)
  4. I/O 操作
    • 文件读写(Node.js 的 fs 模块)
    • 网络请求(fetch/XMLHttpRequest 的回调)
  5. UI 渲染(浏览器环境)
  6. DOM 事件回调
    • click, scroll, resize 等事件处理器
  7. requestAnimationFrame - 浏览器动画回调
  8. MessageChannel 消息通道
  9. 脚本整体代码<script> 标签内的同步代码)

二、微任务(Microtask)

微任务在每个宏任务结束后立即执行,且会清空整个微任务队列(包括执行过程中新产生的微任务)。

常见微任务:

  1. Promise 回调
    • .then()
    • .catch()
    • .finally()
  2. async/await - await 后的代码相当于微任务
  3. queueMicrotask() - 专门的微任务 API
  4. MutationObserver - DOM 变化观察器(浏览器)
  5. process.nextTick() - Node.js 特有(优先级最高)

三、执行顺序图示

   ┌───────────────────────┐
   │     宏任务队列         │
   │ (setTimeout, I/O等)   │<──────────┐
   └──────────┬────────────┘           │
              │ 取出一个任务            │
              ▼                        │
   ┌───────────────────────┐           │
   │   执行当前宏任务       │           │
   └──────────┬────────────┘           │
              │ 产生微任务              │
              ▼                        │
   ┌───────────────────────┐           │
   │  清空所有微任务队列    │──────┐    │
   │ (Promise, await等)    │      │    │
   └──────────┬────────────┘      │    │
              │                   │    │
              ▼                   │    │
   ┌───────────────────────┐      │    │
   │    可选的UI渲染       │      │    │
   └───────────────────────┘      │    │
              │                   │    │
              └───────────────────┘    │
                                       │
             事件循环继续循环───────────┘

四、关键区别总结

特性 宏任务 微任务
执行时机 事件循环每次取一个执行 在宏任务结束后立即全部执行
队列清空 每次只执行队列中第一个任务 清空整个队列(包括新产生的)
优先级 高(在渲染前执行)
阻塞风险 可能阻塞渲染 过长队列会阻塞渲染
典型API setTimeout, I/O, 事件 Promise, queueMicrotask

五、经典面试题分析

console.log('Start'); // 1. 同步宏任务

setTimeout(() => console.log('Timeout'), 0); // 宏任务

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

queueMicrotask(() => console.log('Queue Microtask')); // 微任务

console.log('End'); // 2. 同步宏任务

执行顺序:

  1. Start(同步宏任务)
  2. End(同步宏任务)
  3. Promise 1(微任务)
  4. Queue Microtask(微任务)
  5. Promise 2(微任务)
  6. Timeout(宏任务)

关键点:

  • 所有微任务(包括链式调用产生的)会在当前宏任务结束后一次性执行
  • queueMicrotaskPromise 都属于微任务
  • 微任务执行顺序按入队顺序

六、特殊场景注意

  1. Node.js 差异

    • process.nextTick() 优先级高于 Promise
    • setImmediate()setTimeout(fn, 0) 顺序不确定
  2. 浏览器渲染

    • 微任务在渲染前执行
    • 宏任务在渲染后执行
    button.addEventListener('click', () => {
      Promise.resolve().then(() => console.log('Microtask'));
      console.log('Listener');
    });
    

    点击后输出:ListenerMicrotask(然后才重绘)

  3. 微任务嵌套

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

    输出:AB(微任务队列会完全清空)


©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容