推荐答案
宏任务 (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) 机制。事件循环的核心是维护两个队列:宏任务队列和微任务队列。
- 宏任务队列 (Macrotask Queue):
- 包含由宿主环境(浏览器或Node.js)发起的异步任务,例如定时器、I/O 操作和 UI 渲染等。
- 每次事件循环只会从宏任务队列中取出一个任务执行。
- 微任务队列 (Microtask Queue):
- 包含由 JavaScript 引擎发起的异步任务,例如 Promise 的回调、MutationObserver 的回调等。
- 在每次宏任务执行完毕后,会检查微任务队列,并且会清空微任务队列,然后再进行下一个宏任务。
执行顺序
事件循环的执行顺序如下:
- 执行栈中的同步代码。
- 查找宏任务队列是否有待执行的宏任务。
- 如果宏任务队列不为空,则取出第一个宏任务放入执行栈中执行。
- 执行完当前宏任务后,检查微任务队列是否有待执行的微任务。
- 如果微任务队列不为空,则依次取出微任务放入执行栈中执行,直到微任务队列为空。
- 渲染页面(如果当前宏任务涉及到 UI 更新)。
- 回到步骤 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 的异步编程至关重要,有助于我们编写更高效、更流畅的代码。