Javascript的事件循环

2020-06-16  本文已影响0人  _hider

javascript事件循环是执行栈,还有微任务和宏任务之间的关联,看了很多网上的文章,大多对事件循环秉持的观点如下:

执行栈执行同步代码,宏任务和微任务执行异步代码。执行栈中同步代码执行完毕之后,再执行微任务队列,等微任务队列执行完毕后,再执行宏任务队列,然后再执行微任务对列循环往复执行。

先不说改观点是对是错,先看宏任务和微任务既然都是执行异步代码的消息队列,两者又有什么区别:

宏任务中有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>
  1. 开始script整体代码执行宏任务。
  2. timer1,timer2,timer3是异步代码,依次进入宏任务队列等待执行。
  3. 同步代码入执行栈,输出console1
  4. 接下来是promise1,进入微任务队列并执行,输出promise1
  5. 第一轮宏任务循环结束,开始第二轮宏任务,timer1执行并输出setTimeout1
  6. 第二轮宏任务结束,开始第三轮宏任务,执行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);
  1. 同步代码入执行栈,执行并输出setTimeout2
  2. promise2进入微任务队列开始执行,执行同步代码,依次输出promise35
  3. promise3进入微任务队列开始执行,输出promise4,要注意的是当结束一轮宏任务之后,必须执行当前所有的微任务,才能进入下一轮宏任务。
  4. 然后发现该轮宏任务循环还有个timer4,排入宏任务队列。
  5. 因为队列是先进先出,timer3timer4先进入宏任务队列,所以先执行timer3,输出setTimeout3
  6. 最后执行timer4,输出setTimeout4
上一篇 下一篇

猜你喜欢

热点阅读