在之前的一篇文章中,小次简单的介绍了 Javascript 中的 Event Loop,没看过的小伙伴可以点击下方传送门:
Javascript 基础夯实——理解 Event Loop、Micro Task & Macro Task
上周有一位小伙伴来找小次对这个话题又进行了一番探讨,内容是关于在 Macro Task 执行时,如果有新的 Micro Task 入栈,那么这个新任务会在什么时间执行呢?
下面是一种可能存在的情况:
function macro () {
setTimeout(() => {
micro()
})
}
function micro () {
new Promise.resolve().then(() => {
...
})
}
macro()
可能存在的情况
- 当前 Macro Task 栈中有多个任务时,新的 Micro Task 将会在所有 Marco Task 完成之后的下一次主任务及 UI 渲染之间执行
- 新的 Micro Task 会在当前正在执行的 Macro Task 结束之后执行,并且如果 Micro Task 中有更新 UI 的任务,剩余的 Macro Task 会在 UI 更新完成后执行
当然,情况可能并不只有上面两种。但是可以发现的是,上面两种情况中都没有提到 Main Task 的执行时机,因为只要 Main Task 存在,它就会在当前任务执行完毕后立即执行,优先级是最高的。
下面就验证一下上面两种情况的猜测是否正确。
验证代码
// html
<p id="test"></p>
// javascript
const $test = document.getElementById('test')
let counter = 0
function func1 () {
$test.innerText = ++counter
alert('func1')
}
function func2 () {
$test.innerText = ++counter
alert('func2')
}
function func3 () {
$test.innerText = ++counter
alert('func3')
}
function func4 () {
$test.innerText = ++counter
alert('func4')
}
(function () {
// main task
func1()
// macro task
setTimeout(() => {
func2()
// micro task
Promise.resolve().then(func4)
Promise.resolve().then(func3)
}, 0)
// macro task
setTimeout(func1, 0)
// micro task
Promise.resolve().then(func3)
// main task
func4()
})()
上面的代码中,在 setTimeout 执行时,向 Micro Task 中添加了两个任务,并且不管是执行 Main Task,Macro Task 还是 Micro Task 都会对 UI 进行更新,这也能够更方便我们理清楚 UI 更新的时机。
在代码中使用 alert,是为了能够更明显地看到 UI 更新的情况,因为 alert 会阻塞代码和 UI 的更新
下面直接看上面验证代码的执行情况:
1 - alert func1
2 - alert func4
3 - alert func3
4 - UI update -- counter = 3
5 - alert func2
6 - alert func4
7 - alert func3
8 - UI update -- counter = 6
9 - alert func1
10 - UI update -- counter = 7
总结一下上面验证代码得出的结论:
如果在 Macro 中有新的 Micro 入栈,Micro 的执行总是在 Macro 之前,并且会在 Micro 全部执行完毕后才会更新 UI 和执行下一个 Macro