JS事件循环(Event Loop)机制及面试题分析

1,177 阅读3分钟

1. 事件循环Event Loop 概念介绍

事件循环Event Loop又叫事件队列,两者是一个概念.

官方定义: 事件循环指的是js代码所在运行环境(浏览器、nodejs)编译器的一种解析执行规则。

  • 在js中讨论事件循环是没有意义的, : 事件循环不属于js代码本身的范畴,而是属于js编译器的范畴

通俗的来说, js代码可以理解为是一个人在公司中具体做的事情, 而 事件循环 相当于是公司的一种规章制度。 两者不是一个层面的概念。

2. 事件循环Event Loop 执行机制

说到事件循环,就必须先了解JS代码的分类和执行机制

2.1 JS代码的分类

image.png

同步任务:同步任务不需要进行等待,必须立即看到执行结果,比如console,alert

异步任务:异步任务需要等待一定的时候才能看到结果,比如setTimeout、网络请求

下面是常用的宏任务和微任务代码:

image.png

注意:

  1. new Promise函数体里面的代码是同步代码,只有promise.then()的then括号里面的代码是微任务代码
  new Promise((resolve, reject) => {
            console.log(8); //同步代码
            resolve();
            console.log(9) //同步代码
        }).then(res => {
            console.log(10);//微任务代码
        })
  1. async/await 右边的代码是同步代码, async/await下面的才是微任务代码
async function fnOne() {
            console.log(2); //同步任务
            await fnTwo(); // 同步任务
            console.log(3); //异步微任务
        }
        async function fnTwo() {
            console.log(4);
        }
        fnOne();

2.2 JS事件循环执行机制

image.png

  • 1.进入到script标签,就进入到了第一次事件循环.

  • 2.遇到同步代码,立即执行

  • 3.遇到宏任务,放入到宏任务队列里.

  • 4.遇到微任务,放入到微任务队列里.

  • 5.执行完所有同步代码

  • 6.执行微任务代码

  • 7.微任务代码执行完毕,本次队列清空

  • 寻找下一个宏任务,重复步骤1

    • 以此反复直到清空所以宏任务,这种不断重复的执行机制,就叫做事件循环

了解了js代码的分类和js事件循环的机制之后, 不管多复杂的面试题都能轻轻松松的拿下了.

3. 面试题

3.1 先来个简单的

 <script>

        console.log(1) //同步代码
        setTimeout(function () {
            console.log(2)
            new Promise(function (resolve) {
                console.log(3)
                resolve()
            }).then(function () {
                console.log(4)
            })
        })

        new Promise(function (resolve) {
            console.log(5)
            resolve()
        }).then(function () {
            console.log(6)
        })
        
        setTimeout(function () {
            console.log(7)
            new Promise(function (resolve) {
                console.log(8)
                resolve()
            }).then(function () {
                console.log(9)
            })
        })
        console.log(10)

    </script>

思路分析: 1, 先按照代码类别执行同步代码

image.png

2, 同步代码执行完毕之后,再去执行微任务里面的代码,此时已执行完的代码顺序是这样的:

image.png

3, 微任务的代码执行完毕之后,先执行宏任务里面的第一块代码

image.png

4, 再去执行宏任务里面的第二块代码

image.png

最后,代码的执行顺序如下:

image.png

3.2 再来个难一点的

<script>
    
        console.log(1);
        async function fnOne() {
            console.log(2);
            await fnTwo(); // 右边先执行右侧的代码, 然后等待
            console.log(3);
        }
        async function fnTwo() {
            console.log(4);//
        }
        fnOne();
        
        setTimeout(() => {
            console.log(5);
        }, 2000);

        let p = new Promise((resolve, reject) => { // new Promise()里的函数体会马上执行所有代码
            console.log(6);
            resolve();
            console.log(7);
        })

        setTimeout(() => {
            console.log(8)
        }, 0)
        
        p.then(() => {
            console.log(9);
        })
        console.log(10);
    </script>
    <script>
        console.log(11);
        setTimeout(() => {
            console.log(12);
            let p = new Promise((resolve) => {
                resolve(13);
            })
            p.then(res => {
                console.log(res);
            })
            console.log(15);
        }, 0)
        console.log(14);
    </script>

思路分析: 1, 先判断每个代码是同步还是异步,先执行同步代码

image.png

image.png

image.png

2, 同步代码执行完毕之后再去微任务里面执行微任务代码

image.png

3, 微任务代码执行完毕后,再去执行宏任务里面的代码

注意点: (1)如果页面有多个script标签,多余的script标签可以理解为是一个独立的事件循环, 并且script标签是一个特殊的宏任务,优先级比定时器更高执行

所以此处宏任务队列里面优先执行script标签里面的代码,然后再执行定时器里面的代码

另外微任务的script标签里面既有同步代码,又有异步代码,又需要进行第二轮的代码分类

image.png

接下来,再去执行宏任务里面三个定时器里面的代码

以此类推,最后代码的执行顺序就是这样子滴

image.png

总之一句话,只要搞懂了事件循环机制和js代码分类,再复杂的面试题都能so easy的解答出来!

image.png