Skip to content

宏任务与微任务

约 1400 字大约 5 分钟

宏任务微任务

2025-06-24

宏任务

宏任务的定义

宏任务(Macrotask)是 JavaScript 运行中被调度执行的大颗粒度任务。

页面中的大部分操作都属于宏任务,例如:

  • 页面渲染事件(解析 DOM、计算布局、绘制)。
  • 用户交互事件(鼠标点击、滚动、缩放等)。
  • JavaScript 脚本执行。
  • 网络请求完成、文件读写完成事件。

宏任务通过事件循环(Event Loop)机制进行调度。浏览器会维护多个消息队列,如普通消息队列和延迟执行队列,主线程会通过持续循环从这些队列中取出任务并执行。

宏任务的执行流程

根据 WHATWG 规范,宏任务的执行流程可简化为以下步骤:

  1. 从多个消息队列中选取最早的任务(oldestTask)。
  2. 记录任务的开始时间,将其设置为当前正在执行的任务。
  3. 执行该任务,并将其从队列中移除。
  4. 统计任务执行时长,继续下一任务。

这种执行方式确保了任务的顺序性与稳定性,但也因此存在一定的局限性。

宏任务的局限性

由于消息队列的任务由系统调度,JavaScript 无法细粒度地控制任务的插入顺序。因此,宏任务的执行时间间隔难以精确控制。例如:

  1. 在设置多个 setTimeout 回调时,系统可能在回调任务间插入渲染任务等系统级任务。
  2. 如果插入的任务执行时间过长,会导致后续任务的时延增加,影响整体性能。

宏任务的这种粗时间颗粒度特性,限制了其在高实时性场景中的应用。

微任务

微任务的定义

微任务(Microtask)是运行于 JavaScript 引擎中的细粒度异步任务。它的执行时机是在 当前宏任务结束后、下一个宏任务开始前

微任务的设计初衷是为了解决宏任务时间颗粒度过大的问题。通过微任务,可以更高效地处理需要即时响应的任务。微任务的典型应用包括:

  • Promise 的回调。
  • MutationObserver 监听 DOM 变化。

微任务的执行机制

每个宏任务执行时,会创建一个属于自己的 微任务队列。微任务队列的执行流程如下:

  1. 在当前宏任务执行完成后,检查微任务队列。
  2. 按照顺序执行队列中的所有微任务。
  3. 如果在执行微任务时产生了新的微任务,则将其立即加入队列,并继续执行,直到队列清空。

这意味着微任务的执行优先级高于后续的宏任务。

微任务的来源

微任务主要通过以下两种方式产生:

  1. Promise
    使用 Promise.resolve()Promise.reject() 会立即生成一个微任务,并加入当前的微任务队列。

  2. MutationObserver
    通过监听 DOM 节点的变化,当节点被修改时,会生成一个微任务,记录 DOM 的变化信息。

宏任务与微任务的区别

特性宏任务(Macrotask)微任务(Microtask)
来源渲染事件、网络请求、用户交互、setTimeoutPromiseMutationObserver
执行时机主线程从消息队列中取出任务时执行当前宏任务结束后、下一个宏任务开始前
执行优先级
时间精度精确

宏任务与微任务的关系

每一个宏任务都绑定了一个微任务队列。当一个宏任务执行完成时,JavaScript 引擎会检查并执行该任务中产生的所有微任务。只有微任务队列被清空后,才会开始执行下一个宏任务。

这种机制保证了微任务的高优先级,同时也可能带来一定的问题。例如,在一个宏任务中,如果微任务的数量过多或执行时间过长,可能会显著延长该宏任务的执行时间,从而影响页面的响应性能。

微任务的优势

  1. 高实时性
    微任务的执行时机非常精确,能够在当前宏任务结束后立即执行,适合对时间精度要求高的场景。

  2. 减少性能开销
    微任务在当前宏任务内完成,不需要切换到新的事件循环,能够有效减少性能开销。

  3. 灵活性强
    微任务的执行机制更符合异步任务的需求,广泛应用于现代开发中。

总结

宏任务与微任务是 JavaScript 异步机制的重要组成部分。宏任务适合处理大颗粒度的任务调度,而微任务则通过更高优先级、更精确的执行时机补充了宏任务的不足。在实际开发中,合理利用宏任务与微任务的特性,可以编写出更加高效、现代化的代码。

了解二者的底层原理,不仅有助于你读懂复杂的代码逻辑,还能帮助你写出性能更优的程序。在未来,随着 JavaScript 生态的不断发展,微任务的应用场景和重要性仍会持续增加。