在JavaScript中,微任务(Microtask)和宏任务(Macrotask)是异步任务执行机制的重要组成部分,它们共同构成了JavaScript事件循环(Event Loop)的核心逻辑。理解这两个概念对于编写高性能、无阻塞的代码至关重要。
1. 基本概念
宏任务(Macrotask)
- 定义:由浏览器或Node.js环境提供的异步任务,通常是一些离散的、独立的操作。
- 常见宏任务 :
- setTimeout、- setInterval
- setImmediate(Node.js)
- requestAnimationFrame(浏览器)
- I/O操作(如文件读取、网络请求)
- UI渲染(浏览器)
 
微任务(Microtask)
- 定义:JavaScript引擎自身提供的异步任务,通常是需要在当前任务完成后立即执行的操作。
- 常见微任务 :
- Promise.then()、- Promise.catch()、- Promise.finally()
- MutationObserver(浏览器)
- process.nextTick(Node.js)
- queueMicrotask(ES2020+)
 
2. 执行顺序规则
JavaScript的事件循环执行机制遵循以下规则:
- 执行栈(Call Stack) 为空时,事件循环开始处理任务队列。
- 宏任务队列 每次只取一个任务执行,执行完毕后立即处理 微任务队列。
- 微任务队列 会被清空(依次执行所有微任务),直到队列为空。
- 微任务队列清空后:
- 浏览器可能进行 UI渲染(仅在浏览器环境)。
- 事件循环回到步骤2,继续处理下一个宏任务。
 
总结 :宏任务 → 清空微任务队列 → UI渲染 → 下一个宏任务
3. 经典示例分析
            
            
              javascript
              
              
            
          
          console.log('1. 主线程开始');
// 宏任务1
setTimeout(() => {
  console.log('2. setTimeout 回调执行');
  
  // 微任务1
  Promise.resolve().then(() => {
    console.log('3. setTimeout 内部 Promise 微任务执行');
  });
}, 0);
// 微任务2
Promise.resolve().then(() => {
  console.log('4. 主线程 Promise 微任务执行');
  
  // 微任务3
  queueMicrotask(() => {
    console.log('5. queueMicrotask 微任务执行');
  });
});
console.log('6. 主线程结束');执行顺序解析:
- 
主线程 执行: - 输出 1. 主线程开始
- setTimeout被放入 宏任务队列。
- Promise.then被放入 微任务队列(微任务2)。
- 输出 6. 主线程结束。
 
- 输出 
- 
微任务队列 执行: - 执行微任务2,输出 4. 主线程 Promise 微任务执行。
- queueMicrotask被加入微任务队列(微任务3)。
- 继续执行微任务3,输出 5. queueMicrotask 微任务执行。
 
- 执行微任务2,输出 
- 
宏任务队列 执行: - 执行宏任务1(setTimeout回调),输出2. setTimeout 回调执行。
- Promise.then被加入微任务队列(微任务1)。
- 清空微任务队列,执行微任务1,输出 3. setTimeout 内部 Promise 微任务执行。
 
- 执行宏任务1(
最终输出顺序:
1. 主线程开始
6. 主线程结束
4. 主线程 Promise 微任务执行
5. queueMicrotask 微任务执行
2. setTimeout 回调执行
3. setTimeout 内部 Promise 微任务执行4. 实际应用场景
4.1 微任务的应用
- 
Promise链式调用 :确保回调按顺序执行,不阻塞主线程。 javascriptfetchData() .then(processData) .then(updateUI) .catch(handleError);
- 
DOM变更后立即操作 :使用 MutationObserver监听DOM变化后执行回调。
4.2 宏任务的应用
- 
定时器 :实现延迟执行或周期性任务。 javascriptsetTimeout(() => { // 延迟执行的代码 }, 1000);
- 
分批处理大量数据 :避免长时间阻塞主线程。 javascriptfunction processLargeData(data) { const batchSize = 1000; let index = 0; function processBatch() { const batch = data.slice(index, index + batchSize); // 处理当前批次数据 index += batchSize; if (index < data.length) { setTimeout(processBatch, 0); // 使用宏任务拆分处理 } } processBatch(); }
5. 微任务 vs 宏任务的关键区别
| 特性 | 微任务(Microtask) | 宏任务(Macrotask) | 
|---|---|---|
| 执行时机 | 当前任务结束后立即执行(在渲染前) | 下一次事件循环开始时执行(可能在渲染后) | 
| 队列清空规则 | 一次性清空所有微任务 | 每次只处理一个宏任务 | 
| 触发频率 | 高(可能在一次事件循环中多次触发) | 低(每次事件循环只处理一个) | 
| 典型场景 | Promise回调、DOM变更监听 | 定时器、I/O操作、UI渲染 | 
6. 注意事项
- 微任务阻塞渲染:如果微任务队列过长,会导致UI长时间无法渲染,造成页面卡顿。
- 合理选择任务类型 :
- 需要立即执行的异步操作(如Promise链)使用微任务。
- 需要离散执行的操作(如定时器、大数据处理)使用宏任务。
 
- 浏览器兼容性 :queueMicrotask是较新的API,旧浏览器可能需要使用Promise.resolve().then()替代。
通过理解微任务和宏任务的执行机制,你可以更精确地控制代码的执行顺序,避免性能问题,提升用户体验。