本片文章力求用最简单的语言,分析出:Event Loop、宏任务&微任务、同步任务&异步任务、消息队列&宏任务队列之间的关系。
JavaScript是一门单线程语言,这是这门语言的骨子里的性质。但是我们可以通过浏览器的多核性质,再加上Event Loop(事件循环队列),让单线程的JS也可以实现多线程和异步。
事件循环队列由三个部分组成:调用栈(call stack)、消息队列(Message Queue)、微任务队列(Microtask Queue)。
- 宏任务:script(主程序代码)、setTimeout、setInterval、setImmediate、I/O、UI rendering
- 微任务:process.nextTick、Promises.then、Object.observe、MutationObserver
- 微任务队列:Promise.then、async/swait、process.nextTick创建的异步任务。
- 消息队列:fectch、setTimeout、setInterval等的回调(这些任务并不一定是立即被推入消息队列的,就setTimeout来说,假如定时器设置的时间为5000ms,就会在5000ms之后被推入消息队列中)。
- 同步任务:script中,同步执行的代码。包括如new Promise部分,函数体的同步任务部分。
- 异步任务:消息队列、微任务队列中的任务。
执行栈执行顺序:同步任务 → 微任务队列中的任务 → 消息队列中的任务
在调用栈中,将全局执行上下文中的同步任务依次压入、执行、弹出(期间如果遇到函数,在函数调用时,顺带将函数体所在上下文中的同步任务给执行了)。异步任务中的宏任务(除script)推到消息队列中,微任务推到微任务队列中。待同步任务全部执行完毕后,此时,调用栈为空。接下来,先执行微任务队列中的任务,在微任务队列中的任务执行完后,然后去执行消息队列中的宏任务。这时如果有新的微任务加入,执行完当前宏任务,会先去执行微任务,执行完后,再执行剩下的宏任务。如果加入的是一个宏任务,就排到消息队列的最后面。
常见任务优先级:process.nextTick > Promises.then > setTimeout > setInterval
我们来一起看三个例子:
// 例1:
setTimeout(() => {
console.log(1);
}, 0);
new Promise(resolve => {
console.log(2);
resolve();
console.log(3);
}).then(() => {
console.log(4);
});
console.log(5);
// 2 3 5 4 1
new Promise属于同步任务,先执行。Promise.then、setTimeout都属于异步任务。其次Promise.then又属于微任务,宏任务中的setTimeout会被放到消息队列中,根据上面的执行顺序,就可以知道:
同步任务 → 异步任务 ( 微任务队列 → 消息队列 )
// 例2:
setTimeout(() => {
console.log(1);
},0);
new Promise((resolve,reject) => {
console.log(2);
setTimeout(() => {
resolve();
},0)
}).then(() => {
console.log(3);
}).then(() => {
console.log(4);
});
process.nextTick(() => {
console.log(5);
});
console.log(6);
// 2 6 5 1 3 4
这个例子和例1的区别在于:在promise的构造中,没有同步的resolve,因此promise.then在当前的执行队列中是不存在的,只有promise从pending转移到resolve,才会有then方法,而这个resolve是在一个setTimout时间中完成的,因此3,4最后输出。
// 例3
var p = new Promise(resolve => {
console.log(4);
resolve(5);
});
function fun1() {
console.log(1);
}
function fun2() {
setTimeout(()=>{
console.log(2);
});
fun1();
console.log(3);
p.then(resolved => {
console.log(resolved);
})
.then(() => {
console.log(6);
});
}
func2();
// 4 1 3 5 6 2
参考文章:
GItHub_从promise、process.nextTick、setTimeout出发,谈谈Event Loop中的Job queue
bilibili_2分钟了解 JavaScript Event Loop | 面试必备