关于浏览器的事件循环机制,在之前的话总是感觉模模糊糊,今天特意回顾了一下,彻底搞懂它!卷!
浏览器的事件循环机制(JavaScript Event Loop)
首先,JavaScript是一门单线程的语言,异步和多线程的实现需要借助JavaScript Event Loop 事件循环机制来实现的,大体包括(调用栈、任务队列,任务队列又分为宏任务和微任务)
JavaScript的代码在执行过程中,除了依靠函数调用栈来搞定函数的执行顺序外话需要靠任务队列来搞定另外一些代码的执行。整个执行过程,我们称为事件循环的过程。一个线程中,事件循环是惟一的,但是任务队列可以拥有多个。任务队列又分为宏任务(macro-task)和微任务(micro-task),在最新标准中他们又被分为task和jobs。
执行宏任务,然后执行宏任务产生的微任务,若微任务在执行的过程中产生了新的微任务,则继续执行微任务;微任务执行完毕后,在回到宏任务进行下一轮的循环。
宏任务包括
- script标签整体代码
- setTimeout、setInterval、setImmediate
- fetch
微任务包括
- promise
- async和await
- process.nextTick
事件开始时,会先从全局栈开始一行一行执行,遇到函数调用会将其压入调用栈中,被压入的函数叫做帧,但函数返回后,会将函数从栈中弹出。
接下来我们直接上代码,然后一步一步拓展
<script>
function func1() {
console.log(1);
}
function func2() {
console.log(2);
func1()
console.log(3);
}
func2()
</script>
分析
- 首先,调用func2函数,将func2压入栈中,执行它里边的代码,遇到
console.log(2); ,压入栈中,打印执行,输出2,然后弹出。 - 然后,遇到func1函数,将func1压入栈中,执行里边的代码,遇到
console.log(1); ,压入栈中,打印执行,输出1,然后弹出。func1函数执行完毕弹出。 - 最后,遇到
console.log(3); ,压入栈中,打印执行,输出3,弹出,func2执行完毕,弹出,此时整个调用栈清空。
所以最后的输出是2 1 3。
接着在以上代码中加上宏任务
在遇到宏任务时,会将其入队到任务队列中等待,任务队列会在调用栈清空时执行!
<script>
function func1() {
console.log(1);
}
function func2() {
setTimeout(() => {
console.log(2);
}, 0);
func1()
console.log(3);
}
func2()
</script>
分析
- 首先执行func2函数,将其压入栈中
- 在遇到
setTimeout、setInterval、fetch 时,会先将其入队到任务队列当中 - 任务队列会在调用栈清空时执行,所以
console.log(2) 先入队等待,然后遇到func1()调用,将func1函数压入栈中,开始执行,将console.log(1) 压入栈中,打印执行,输出1,然后弹出,然后弹出func1,然后将console.log(3) 压入栈中,打印执行,输出3 - 此时调用栈已清空,任务队列中的任务会被压入栈中执行,然后输出2
在宏任务的基础上再加上微任务
Promise、async、await异步操作会在调用栈清空时立即执行,并且新加入的微任务也会被一同执行!!!
<script>
var p = new Promise(resolve=>{
console.log(4);
resolve(5)
})
function func1() {
console.log(1);
}
function func2() {
setTimeout(() => {
console.log(2);
}, 0);
func1()
console.log(3);
p.then(data=>{
console.log(data);
})
.then(()=>{
console.log(6);
})
}
func2()
</script>
分析
- 首先,会加载Promise的构造函数,将其压入栈中,执行里边的代码,
console.log(4)、resolve(5) 完成后将其弹出栈。 - 之后将func2压入栈中,开始执行里边的代码,遇见宏任务,会先将它入队到任务队列中等待,然后执行func1,打印1后将其弹出,之后再打印3
- 紧接着就产生了微任务p.then()的方法,会先将其加入到微任务,然后瞬间执行
resolve(5)、console.log(6) ,然后将微任务弹出后,再回到宏任务,执行延迟器console.log(2) ,执行完毕后弹出
所以最后的结果就是4 1 3 5 6 2
|