宏任务和微任务

微任务

每次当一个任务退出且执行上下文为空的时候,微任务队列中的每一个微任务会依次被执行。不同的是它会等到微任务队列为空才会停止执行——即使中途有微任务加入。换句话说,微任务可以添加新的微任务到队列中,并在下一个任务开始执行之前且当前事件循环结束之前执行完所有的微任务。

宏任务

当执行来自任务队列中的任务时,在每一次新的事件循环开始迭代的时候运行时都会执行队列中的每个任务。在每次迭代开始之后加入到队列中的任务需要在下一次迭代开始之后才会被执行.

三原则

  1. 任务开始于同步代码结束时
  2. 同一个层级微任务比宏任务先执行
  3. 每个宏任务对应一个单独的微任务队列

分类

api 任务类型 浏览器 node
I/O 宏任务 支持 支持
setTime 宏任务 支持 支持
setInterval 宏任务 支持 支持
setImmediate 宏任务 不支持 支持
requestAnimationFrame 宏任务 支持 不支持
process.nextTick 微任务 不支持 支持
MutationObserver 微任务 支持 不支持
promise.then catch finally 微任务 支持 支持

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function app() {
setTimeout(() => {
console.log("1-1");
Promise.resolve().then(() => {
console.log("2-1");
});
});
console.log("1-2");
Promise.resolve().then(() => {
console.log("1-3");
setTimeout(() => {
console.log("3-1");
});
});
}
app();

1-2 => 1-3 => 1-1 => 2-1 => 3-1

说明
同步代码执行完成后,执行上下文为空,开始执行微任务, 微任务内的宏任务会添加到队列中等待下一次迭代时被执行,开始执行异步代码,内部的微任务在该执行上下文结束后执行,添加到队列中的宏任务开始执行

异步函数示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(async function() {
setTimeout(() => {
console.log("1-1");
Promise.resolve().then(() => {
console.log("2-1");
});
});
console.log("1-2");
await Promise.resolve().then(() => {
console.log("1-3");
setTimeout(() => {
console.log("3-1");
});
});
console.log("1-4");
Promise.resolve().then(() => {
console.log("1-5")
});
})()

微任务实现

浏览器实现微任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function microTask(fn) {
let counter = 1;
const textNode = document.createTextNode(counter.toString());
const observer = new MutationObserver(fn);
observer.observe(textNode, {
characterData: true
})
counter = 0;
textNode.data = counter.toString()
}

(function () {
setTimeout(() => {
console.log("1-1");
}, 0)
console.log("1-2");
microTask(() => {
console.log("1-3")
})
console.log("1-4")
})();

node微任务

1
2
3
4
5
6
7
8
9
10
(function () {
setTimeout(() => {
console.log("1-1")
}, 0)
console.log("1-2")
process.nextTick(() => {
console.log("1-3")
})
console.log("1-4")
})()