什么是宏任务 (Macrotask) 和微任务 (Microtask)?请分别列举一些常见的宏任务和微任务。

推荐答案

宏任务 (Macrotask)

宏任务是由宿主环境(浏览器或Node.js)发起的任务,它们会被添加到宏任务队列中,每次事件循环只执行一个宏任务。常见的宏任务包括:

  • setTimeout
  • setInterval
  • setImmediate (Node.js 环境)
  • requestAnimationFrame
  • I/O 操作 (如文件读写,网络请求)
  • UI 渲染

微任务 (Microtask)

微任务是由 JavaScript 引擎发起的任务,它们会被添加到微任务队列中,每次宏任务执行完毕后,会清空微任务队列。常见的微任务包括:

  • Promise.then (包括 .catch.finally)
  • MutationObserver
  • process.nextTick (Node.js 环境)
  • queueMicrotask (较新的 API)

本题详细解读

任务队列和事件循环

JavaScript 是单线程的,为了处理异步操作,它使用事件循环 (Event Loop) 机制。事件循环的核心是维护两个队列:宏任务队列和微任务队列。

  1. 宏任务队列 (Macrotask Queue)
    • 包含由宿主环境(浏览器或Node.js)发起的异步任务,例如定时器、I/O 操作和 UI 渲染等。
    • 每次事件循环只会从宏任务队列中取出一个任务执行。
  2. 微任务队列 (Microtask Queue)
    • 包含由 JavaScript 引擎发起的异步任务,例如 Promise 的回调、MutationObserver 的回调等。
    • 在每次宏任务执行完毕后,会检查微任务队列,并且会清空微任务队列,然后再进行下一个宏任务。

执行顺序

事件循环的执行顺序如下:

  1. 执行栈中的同步代码。
  2. 查找宏任务队列是否有待执行的宏任务。
  3. 如果宏任务队列不为空,则取出第一个宏任务放入执行栈中执行。
  4. 执行完当前宏任务后,检查微任务队列是否有待执行的微任务。
  5. 如果微任务队列不为空,则依次取出微任务放入执行栈中执行,直到微任务队列为空。
  6. 渲染页面(如果当前宏任务涉及到 UI 更新)。
  7. 回到步骤 2,重复上述过程。

宏任务和微任务的区别

特性 宏任务 (Macrotask) 微任务 (Microtask)
发起者 宿主环境 (浏览器或 Node.js) JavaScript 引擎
添加队列 宏任务队列 微任务队列
执行时机 每次事件循环只执行一个 每次宏任务执行完后清空
优先级
常见示例 setTimeout, setInterval, requestAnimationFrame, I/O, UI 渲染 等 Promise.then, MutationObserver, process.nextTick, queueMicrotask
影响 可能导致页面卡顿 影响较小,可以提高性能

为什么要区分宏任务和微任务?

区分宏任务和微任务的主要目的是为了提高 JavaScript 的执行效率和用户体验。

  • 保证 UI 渲染的及时性:UI 渲染通常在宏任务中执行,如果在微任务中进行大量的计算或操作,会导致 UI 渲染被延迟,从而引起页面卡顿。将复杂的计算和异步操作放在微任务中,可以在宏任务完成后立即执行,从而避免阻塞 UI 渲染。
  • 提高性能:微任务的优先级较高,可以保证异步操作的结果及时处理,避免不必要的等待时间。例如,Promise 的链式调用可以连续执行多个微任务,从而避免多次事件循环的开销。
  • 精细控制异步操作:开发者可以通过选择使用宏任务还是微任务,来更精细地控制异步操作的执行顺序。

了解宏任务和微任务的执行机制对于理解 JavaScript 的异步编程至关重要,有助于我们编写更高效、更流畅的代码。

纠错
反馈