微观任务(Microtasks)和宏观任务(Macrotasks)在JavaScript的事件循环机制中扮演着不同的角色,并且它们的执行顺序有着明确的区别。下面我将详细介绍这两类任务的执行顺序差异:
宏观任务 (Macrotasks)
宏观任务通常包括:
setTimeout
和setInterval
(定时器)setImmediate
(仅Node.js环境)- I/O操作
- UI渲染
fetch
请求XMLHttpRequest
readFile
/writeFile
等文件操作
宏观任务的特点:
- 每个宏观任务都会创建一个新的执行上下文。
- 宏观任务的执行会阻塞后续宏观任务的执行,直到当前宏观任务完成。
- 当一个宏观任务执行完毕后,会执行所有已存在的微观任务。
- 宏观任务之间是串行执行的,即一个宏观任务执行完毕后才会执行下一个宏观任务。
微观任务 (Microtasks)
微观任务通常包括:
Promise
的.then
方法MutationObserver
(某些浏览器环境)process.nextTick
(Node.js环境)queueMicrotask
(现代浏览器)
微观任务的特点:
- 微观任务在当前宏任务执行完成后立即执行。
- 微观任务在下一个宏任务开始之前执行。
- 微观任务中如果有新的微观任务被添加,这些新的微观任务也将在当前微观任务执行完成后立即执行。
- 微观任务的执行不会创建新的执行上下文。
执行顺序
在JavaScript中,执行顺序遵循以下规则:
- 同步任务:首先执行所有的同步任务。
- 微观任务:一旦所有的同步任务执行完毕,就会执行所有的微观任务,直到微观任务队列为空。
- 宏观任务:当微观任务队列为空后,如果队列中有待执行的宏观任务,则开始执行下一个宏观任务。每个宏观任务执行完毕后,会再次执行所有已存在的微观任务,直到微观任务队列再次变为空。
这个过程会一直持续,直到所有的宏观任务和微观任务都被处理完毕。
示例
假设我们有以下JavaScript代码:
console.log('script start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('promise1');
Promise.resolve().then(() => {
console.log('promise2');
});
});
console.log('script end');
setImmediate(() => {
console.log('setImmediate');
});
输出结果将是:
script start
script end
promise1
promise2
setTimeout
setImmediate
解释:
- 同步任务 (
script start
,script end
) 首先被执行。 - 微观任务 (
promise1
,promise2
) 在同步任务完成后立即执行。- 注意
promise1
中的Promise.resolve().then(() => { console.log('promise2'); })
也会被添加到微观任务队列中,并在promise1
之后立即执行。
- 注意
- 宏观任务 (
setTimeout
,setImmediate
) 按照它们被加入到队列的顺序执行。
总结一下,微观任务具有更高的优先级,在当前宏观任务执行完成后立即执行,并且在下一个宏观任务开始之前执行。这使得微观任务非常适合用于需要快速响应的场景,比如处理异步数据的结果。