首页
/ MDN项目:深入理解JavaScript微任务(Microtask)机制

MDN项目:深入理解JavaScript微任务(Microtask)机制

2025-07-07 01:01:44作者:俞予舒Fleming

什么是微任务?

微任务(Microtask)是JavaScript中一种特殊的异步任务类型,它会在当前函数或程序执行完成后立即执行,但必须满足两个条件:

  1. JavaScript执行栈为空
  2. 在将控制权交还给用户代理(如浏览器)的事件循环之前

微任务与常规任务(宏任务)的关键区别在于执行时机。微任务会在当前任务结束后、下一个任务开始前执行,这使得它们非常适合用于需要立即执行但又不能阻塞主线程的场景。

微任务与宏任务的对比

宏任务(Macrotasks)

宏任务包括:

  • 整个脚本的执行
  • 事件回调(如点击事件)
  • setTimeout/setInterval回调
  • I/O操作
  • UI渲染

宏任务会被放入任务队列,事件循环每次从队列中取出一个任务执行。

微任务(Microtasks)

微任务包括:

  • Promise回调
  • MutationObserver回调
  • queueMicrotask添加的任务

微任务会在当前宏任务结束后立即执行,且会一次性执行完所有微任务队列中的任务(包括在执行过程中新添加的微任务)。

为什么需要微任务?

微任务机制主要解决两个问题:

  1. 执行顺序保证:确保某些操作在特定时机执行
  2. 批量处理:将多个操作合并为一次处理

典型应用场景包括:

  • Promise的实现
  • DOM变更观察(MutationObserver)
  • 需要确保在UI更新前完成的异步操作

如何使用queueMicrotask

现代浏览器提供了queueMicrotaskAPI来显式创建微任务:

queueMicrotask(() => {
  console.log('这个回调会在当前任务结束后立即执行');
});

与使用Promise创建微任务相比,queueMicrotask有以下优势:

  1. 更直观的语义
  2. 异常会直接抛出而不是被Promise捕获
  3. 性能更好(不需要创建Promise实例)

实际应用示例

1. 条件性Promise的顺序保证

class DataLoader {
  constructor() {
    this.cache = new Map();
  }

  loadData(url) {
    if (this.cache.has(url)) {
      queueMicrotask(() => {
        this.data = this.cache.get(url);
        this.dispatchEvent(new Event('load'));
      });
    } else {
      fetch(url)
        .then(response => response.json())
        .then(data => {
          this.cache.set(url, data);
          this.data = data;
          this.dispatchEvent(new Event('load'));
        });
    }
  }
}

这个例子确保了无论数据是否缓存,load事件都会在相同的时间点触发。

2. 批量操作处理

const messageBatch = [];

function sendAnalytics(data) {
  messageBatch.push(data);
  
  if (messageBatch.length === 1) {
    queueMicrotask(() => {
      // 批量发送所有收集的数据
      fetch('/analytics', {
        method: 'POST',
        body: JSON.stringify(messageBatch)
      });
      messageBatch.length = 0; // 清空批次
    });
  }
}

这种模式可以有效减少网络请求次数,提高性能。

注意事项

  1. 避免微任务无限循环:微任务中可以添加新的微任务,如果不加控制会导致无限循环
  2. 不要过度使用:微任务虽快,但大量使用仍可能阻塞主线程
  3. 理解执行顺序:微任务会在当前宏任务结束后立即执行,早于下一个宏任务

微任务执行流程示例

console.log('脚本开始'); // 宏任务

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

Promise.resolve().then(() => {
  console.log('Promise微任务');
});

queueMicrotask(() => {
  console.log('queueMicrotask微任务');
});

console.log('脚本结束'); // 宏任务

输出顺序:

  1. 脚本开始
  2. 脚本结束
  3. Promise微任务
  4. queueMicrotask微任务
  5. setTimeout

总结

微任务是JavaScript异步编程中的重要概念,理解它们的工作机制对于编写高效、可靠的异步代码至关重要。queueMicrotaskAPI为开发者提供了直接操作微任务队列的能力,但在大多数情况下,使用Promise等高级抽象更为合适。合理使用微任务可以优化性能,但要注意避免滥用。