Javascript 基础夯实——理解 Event Loop、M
首先就标题上的三个名词做个名词解释:)
Event Loop:事件循环
Micro Task:微任务
Macro Task:宏任务
所以这次的主体呼之欲出:理解 Javascript 中的事件循环、微任务与宏任务机制
在开始本文之前,小伙伴们可以先去看之前的一篇文章,在这篇文章中略微提及了本篇所述的内容:
Tasks & Stack
Javascript 中的事件循环是以任务为单位的,将很多个待执行的任务串联在一起就形成了队列Task Queue,很多的队列先后按顺序执行任务就形成了Event Loop,所以要理解 Event Loop,就需要先了解 task 的执行过程,请看下图:
Micro Task
我们知道调用alert
时会阻塞页面的渲染,其实这是因为渲染页面总是在任务队列全部执行结束后才会开始,用示意图表示如下:
那么 Micro Task 又是什么执行呢?Micro Task 也是在任务队列执行结束后才执行(或者栈为空时),但是却会先于渲染页面,它就像一条挂在任务队列后面的小尾巴匆匆执行完自己的任务,但是如果这条小尾巴太过繁重,也会影响到页面的渲染
Micro Task 会维护一条自己的队列,同样也存在入栈和出栈的操作
那么有哪些操作是 Micro Task 呢?Promise 就是其中之一
Macro Task
Marco Task 会在当前所有的任务全部执行结束后执行,也就是说,如果当前存在渲染页面,Marco Task 会在页面渲染结束之后执行,所以 Macro Task 不会导致页面的阻塞
我们常用的setTimeout
就是 Macro Task,同时setImmediate
等也是,这也是我们总是将alert
写在setTimeout
中的原因
举例论证:)
�
function func1 () {
console.log('func 1')
}
function func2 () {
console.log('func 2')
}
function func3 () {
console.log('func 3')
}
function func4 () {
console.log('func 4')
}
const func = function () {
func1()
setTimeout(func2, 0)
Promise.resolve().then(
() => func3()
)
func4()
}()
上面一串代码执行后,在控制台会按如下顺序打印出以下内容:
func1
func4
func3
func2
其实根据上文的内容很容易就能够推断出打印内容的顺序,但是我们依然对其进行从入栈和出栈的角度分析一下
// 任务队列
// 开始时无任务
task queue: []
// micro task queue
micro queue: []
// macro task queue
macro queue: []
// 栈
stack: []
执行立即执行函数func
,func
入栈:
task queue: [func]
micro queue: []
macro queue: []
stack: [func]
执行func1
时,func1
入栈:
task queue: [func, func1]
micro queue: []
macro queue: []
stack: [func, func1]
func1
执行结束,出栈:
task queue: [func]
micro queue: []
macro queue: []
stack: [func]
setTimeout
进入 marco task,不执行其回调:
task queue: [func]
micro queue: []
macro queue: [setTimeout]
stack: [func]
promise
进入 micro task,不执行其回调
task queue: [func]
micro queue: [promise]
macro queue: [setTimeout]
stack: [func]
执行func4
并入栈:
task queue: [func, func4]
micro queue: [promise]
macro queue: [setTimeout]
stack: [func, func4]
func4
执行结束并出栈:
task queue: [func]
micro queue: [promise]
macro queue: [setTimeout]
stack: [func]
func
执行结束并出栈:
task queue: []
micro queue: [promise]
macro queue: [setTimeout]
stack: []
此时 stack 已空,任务全部执行结束,开始执行 micro task promiseThen
:
task queue: []
micro queue: [promise]
macro queue: [setTimeout]
stack: [promiseThen]
promiseThen
执行结束出栈:
task queue: []
micro queue: []
macro queue: [setTimeout]
stack: []
如果需要重新渲染页面的话,则会在此时执行,如果不需要,则开始执行 macro tasksetTimeoutCallback
task queue: []
micro queue: []
macro queue: [setTimeout]
stack: [setTimeoutCallback]
setTimeoutCallback
执行结束出栈:
task queue: []
micro queue: []
macro queue: []
stack: []
以上就是这个简单的例子的任务执行顺序
Event Loop
看到这里,你应该已经知道 Event Loop 是什么了,上面的例子是 Event Loop 中的一条,而执行完这一整条之后,还会有下一条,下下条,它们在一起就组成了完整的 Event Loop 机制,周而复始不断循环
当然,关于 Event Loop 的解释并不只有这一种,但是本质上都是相同的,micro task 和 macro task 会区别于 主任务 / 主线程 的队列,并且在程序的整个生命周期中不停的重复 主任务 ——> micro task ——> 渲染视图 ——> macro task 的操作
最后再厚颜无耻的求下关注:)
扫码关注前端周记公众号