在JavaScript的事件循环中,任务被分为两大类:宏任务(Macro-tasks)和微任务(Micro-tasks)。这两类任务决定了JavaScript代码的执行顺序以及何时进行UI渲染、事件处理等。
宏任务(Macro-tasks)
宏任务是指在下一个事件循环迭代中执行的任务。每次执行栈清空时,事件循环都会从宏任务队列中取出一个宏任务来执行。常见的宏任务包括:
setTimeout
setInterval
setImmediate
(主要在Node.js中使用)- I/O 操作
- UI 渲染(浏览器环境)
当一个宏任务执行完成后,浏览器可以选择进行UI渲染,然后事件循环会检查是否有微任务需要执行。
微任务(Micro-tasks)
微任务通常是需要立即执行的短小任务,它们在当前事件循环迭代的末尾执行,但在下一个宏任务开始之前。这意味着微任务的执行优先级高于宏任务。常见的微任务包括:
Promise.then
、Promise.catch
和Promise.finally
回调MutationObserver
回调process.nextTick
(Node.js)
当当前宏任务执行完毕后,事件循环会执行所有队列中的微任务。只有当微任务队列完全清空后,事件循环才会移动到下一个宏任务。
执行顺序
- 执行一个宏任务(从宏任务队列中取出)
- 执行所有微任务(依次从微任务队列中取出,直到队列清空)
- 在执行微任务期间产生的新的微任务也会被添加到队列中,并在当前事件循环迭代中执行
- 可能的UI渲染
- 重复上述步骤,执行下一个宏任务
示例
console.log('脚本开始'); // 同步代码,立即执行
setTimeout(() => {
console.log('setTimeout'); // 宏任务
}, 0);
Promise.resolve().then(() => {
console.log('Promise 1'); // 微任务 1
}).then(() => {
console.log('Promise 2'); // 微任务 2
});
console.log('脚本结束'); // 同步代码,立即执行
执行顺序将是:
- “脚本开始”
- “脚本结束”
- “Promise 1”(所有同步任务完成后,执行微任务)
- “Promise 2”(上一个微任务完成后,继续执行微任务)
- “setTimeout”(所有微任务完成后,执行下一个宏任务)
重要性
理解宏任务和微任务以及它们在事件循环中的角色对于编写高效且响应良好的JavaScript代码至关重要。这有助于开发者优化应用性能,避免潜在的性能瓶颈,并确保异步操作的正确顺序。