javascript事件循环是执行栈,还有微任务和宏任务之间的关联,看了很多网上的文章,大多对事件循环秉持的观点如下:
执行栈执行同步代码,宏任务和微任务执行异步代码。执行栈中同步代码执行完毕之后,再执行微任务队列,等微任务队列执行完毕后,再执行宏任务队列,然后再执行微任务对列循环往复执行。
先不说改观点是对是错,先看宏任务和微任务既然都是执行异步代码的消息队列,两者又有什么区别:
- 宏任务
macrotask:script(整体代码),setTimeout,setInterval,setImmediate,I/O,UI rendering。 - 微任务
microtask:process.nextTick,Promise,Object.observe(已废弃)。
宏任务中有script(整体代码),这个就比较疑惑了?如果script(整体代码)是宏任务的话,那肯定是宏任务先执行,因为script(整体代码)在一开始就会执行。在之前的观点中明明是微任务先执行,然后再执行宏任务,这个就比较矛盾了。所以以上的观点是值得商榷的,事件循环从script(整体代码)开始第一次循环。之后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),然后执行所有的微任务。当所有可执行的微任务执行完毕之后。循环再次从宏任务开始,找到其中一个任务队列执行完毕,然后再执行所有的微任务,这样一直循环下去。
看一道经典的面试题来加深事件循环的理解:
<script>
let timer1 = setTimeout(() => console.log('setTimeout1'), 0); //1宏任务
let timer2 = setTimeout(() => { //2宏任务
console.log('setTimeout2');
let promise2 = Promise.resolve().then(() => { //2微任务
console.log('promise3');
let promise3 = Promise.resolve().then(() => { //3微任务
console.log('promise4');
});
console.log(5);
});
let timer4 = setTimeout(() => console.log('setTimeout4'), 0); //4宏任务
}, 0);
let timer3 = setTimeout(() => console.log('setTimeout3'), 0); //3宏任务
console.log("console1")
let promise1 = Promise.resolve().then(() => { //1微任务
console.log('promise1');
});
</script>
- 开始
script整体代码执行宏任务。 -
timer1,timer2,timer3是异步代码,依次进入宏任务队列等待执行。 - 同步代码入执行栈,输出
console1。 - 接下来是
promise1,进入微任务队列并执行,输出promise1。 - 第一轮宏任务循环结束,开始第二轮宏任务,
timer1执行并输出setTimeout1。 - 第二轮宏任务结束,开始第三轮宏任务,执行
timer2,代码如下:
let timer2 = setTimeout(() => { //2宏任务
console.log('setTimeout2');
let promise2 = Promise.resolve().then(() => { //2微任务
console.log('promise3');
let promise3 = Promise.resolve().then(() => { //3微任务
console.log('promise4');
});
console.log(5);
});
let timer4 = setTimeout(() => console.log('setTimeout4'), 0); //4宏任务
}, 0);
- 同步代码入执行栈,执行并输出
setTimeout2。 -
promise2进入微任务队列开始执行,执行同步代码,依次输出promise3,5。 -
promise3进入微任务队列开始执行,输出promise4,要注意的是当结束一轮宏任务之后,必须执行当前所有的微任务,才能进入下一轮宏任务。 - 然后发现该轮宏任务循环还有个
timer4,排入宏任务队列。 - 因为队列是先进先出,
timer3比timer4先进入宏任务队列,所以先执行timer3,输出setTimeout3。 - 最后执行
timer4,输出setTimeout4。













网友评论