EventLoop事件循环

本文详细介绍了JavaScript中的EventLoop事件循环机制,包括JS的单线程特性、任务队列、宏任务与微任务的区别以及它们的执行顺序。通过实例解析,帮助读者掌握同步代码、异步代码(包括Promise和async/await)的执行流程,从而更好地理解异步编程的本质。
摘要由CSDN通过智能技术生成

目标

理解浏览器中的EventLoop(事件循环)的概念

理解

       js是单线程的,一次只能做一件事。js在浏览器这个宿主环境中运行。浏览器是多线程的,用户交互,定时器,网络请求等等浏览器中的事件会产生对应的任务,任务多了要在任务队列中排队,浏览器的主线程依次取出任务来执行,此过程不断重复从而形成一个循环,称为eventLoop。

js代码分类

     1.同步代码:

                  除了异步代码剩余的就是同步代码

     2.异步代码(异步任务):

                 不是马上执行,是放入到队列中等待;如果所有的任务都要按序等待,那么也不行,需要有一个能插队的机制。所以又将异步任务分为微任务和宏任务,同时对应微任务队列宏任务队列

       微任务队列和宏任务队列:

                  (1)宏任务:

                                        script标签

                                        事件处理函数

                                        ajax请求

                                        定时器(setTimout)

                 (2)微任务:

                                        async / await

                                        promise的then

事件循环规则 

            1.从上往下逐行 "解析"代码

            2.判断代码是同步还是异步

            3.如果是同步, 则立即执行

            4.如果是异步, 则不会立即执行. 微任务就放入微任务队列, 宏任务就会放入宏任务队列

            5.等所有同步执行完毕才会执行异步

            6.先执行异步队列中的微任务, 再执行宏任务, 完成本次事件循环

            7.按照上面的规则重复解析宏任务(事件循环)

案例

1.简单写写:

<script>
       
        console.log(1)

        setTimeout(function () {
            console.log(2)
        }, 0)

        const p = new Promise((resolve, reject) => {
            resolve(1000)
        })
        
        p.then(data => {
            console.log(data)
        })

        console.log(3)

</script>

解析:

在代码执行过程中,先执行同步代码,并输出结果;遇到异步代码之后,交由浏览器(宿主环境)处理 ===> 事件触发之后进入到任务队列继续执行后续代码

所以按照从上往下的顺序依次解析:

(1)同步代码直接执行,所以直接输出:1

console.log(1)

(2)定时器属于异步任务中的宏任务,所以放入宏任务队列等同步代码和微任务先执行完,再执行

setTimeout(function () {
            console.log(2)
        }, 0)

(3)Promise的then属于异步任务中的微任务,所以放入微任务队列等同步代码先执行完,再执行。(p的结果为resolve,执行then,data = 1000)

const p = new Promise((resolve, reject) => {
            resolve(1000)
        })
        
        p.then(data => {
            console.log(data)
        })

(4)同步代码直接执行,所以直接输出:3

console.log(3)

(5)代码解析完毕,其中同步代码直接执行,控制台先输出结果为:1、3;按循环顺序同步执代码行完毕后再执行异步代码,异步代码又先执行微任务队列,所以控制台输出结果:1000,最后执行宏任务队列,输出结果:2.

(6)代码执行完毕,控制台输出结果为: 1、3、1000、2

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) => { 
            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)在有多个script标签的情况下,根据规则将script放入宏任务队列,从上往下依次执行

(2)执行第一个script 

(3) 同步先执行,输出结果为:1

console.log(1);

(2)fnOne()调用函数,输出结果:2;调用fnTwo()输出结果:4;await关键字: (1)取代then  (2)获取then结果,所以这里的console.log(3)等价于.then(res  => {console.log(3)}),将其移入到微任务队列等待执行

    async function fnOne() {
            console.log(2);
            await fnTwo(); 
            console.log(3);
        }
        async function fnTwo() {
            console.log(4);
        }
        fnOne();

(3)定时器移入宏任务队列

(4) new Promise()里的函数体会马上执行所有代码,输出结果:6、7

 

(5)定时器移入宏任务队列

 setTimeout(() => {
            console.log(8)
        }, 0)

(6)上面p的结果为resolve(),.then可以执行,所以p.then移入微任务队列等待

 (7)同步直接执行,输出结果:10

console.log(10); 

(8)同步执行完毕,再执行微任务,输出结果:3、9

(9)微任务执行完毕,再执行宏任务(定时器设置了时间,就按照时间先后排列)

(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>

(11)再次按照规则执行,同步直接执行,输出结果:11

console.log(11);

(12)定时器移入宏任务

    setTimeout(() => {
            console.log(12);
            let p = new Promise((resolve) => {
                resolve(13);
            })
            p.then(res => {
                console.log(res);
            })
            console.log(15);
        }, 0)

(13)同步直接执行,输出结果:14

console.log(14);

(14)没有微任务,就直接执行宏任务

(15)从上往下先执行,第一个定时器,输出结果:

    setTimeout(() => {
            console.log(8)
        }, 0)

(16)执行第二个定时器

(17)同步直接执行,输出结果:12 

console.log(12);

(18)p.then属于微任务,移入微任务队列,等待执行

(19)同步直接执行,输出结果:15

console.log(15);

(20)同步执行完毕,先执行微任务,p成功(resolve)执行res = 13,输出结果为:13

(21)微任务执行完毕,再执行宏任务,输出结果为:5

(22)代码执行完毕,控制台输出结果为: 1、2、4、6、7、10、3、9、11、14、8、12、15、13、5

 3.练练看

    <script>
        console.log(1)
        new Promise((resolve, reject) => {
            resolve(2)
        }).then(res => {
            console.log(res)
        })
        setTimeout(() => {
            console.log(3)
        })
        console.log(4)
    </script>

    <script>
        console.log(5)
    </script>

    <script>
       new Promise((resolve,reject)=>{
           resolve(6)
       }).then(res=>{
           console.log(res)
       })
       setTimeout(()=>{
           console.log(7)
       })
       console.log(8)
    </script>

    <script>
        console.log(9)
        setTimeout(()=>{
           console.log(10)
       })
    </script>

可以自己解析一下

代码执行完毕,控制台输出结果为: 1、 4 、2、 5、 8 、6 、9、 3、 7、 10

结语

        根据循环规则,一步一步的解析代码,EventLoop事件循环就很容易理解。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值