任务队列分类和事件循环

120 阅读6分钟

任务队列分类

任务

任务就是js代码中运行的代码

//fn() 代表调用函数fn的任务,会运行fn函数内部的函数体
//脚本(scrip标签)也是一个任务,计时器的运行和创建promise对象等都是任务

任务分为同步任务和异步任务

//任务
        //fn() 代表调用函数fn的任务,会运行fn函数内部的函数体
        //脚本(scrip标签)也是一个任务,计时器的运行和创建promise对象等都是任务
        //同步任务
        function fg(){};
        var a=new Array();
        var b=fn();
        var p1=new Promise((n1,n2)=>{});
        //以上就是同步任务,需要上一行的任务运行完后,才会运行下一行的任务

        //异步任务
        setTimeout(fg,1000);
        p1.then(fg);
        console.log(666777);
        //setTimeout()也是一个任务,在等1000毫秒以后去运行另一个任务就是调用传入的回调函数fg
        //setTimeout()运行函数大括号内部的代码是底层语言会开启新的线程去运行任务,setTimeout()就是异步任务

异步任务不会阻塞代码的运行,上一行的异步任务代码和下一行的异步任务代码会开启新的线程运行异步任务中的任务代码,而js单线程上紧跟着的同步任务代码也会同时运行。当新线程上异步任务代码运行完后就会调用回调函数,就会回到js单线程上运行回调函数的任务代码,而异步任务中谁先回到js单线程调用回调函数,谁的回调函数的任务代码就会先运行。

        setTimeout(() => {
            console.log(666);
        }, 1000);
        setTimeout(() => {
            console.log(777);
        }, 2000);

第一个异步任务在新线程上先运行完,所以第一个异步任务先回到js单线程,则其回调函数的任务代码先运行,所以控制台先打印666,再打印777.

image.png

        setTimeout(() => {
            console.log(1);
        }, 1000);
        setTimeout(() => {
            console.log(2);
        }, 1000);

当两个异步任务在新线程上完成自己的任务代码后同时回到js单线程运行回调函数的任务代码时,谁先在js单线程上添加进任务列表就谁先运行任务代码,所以控制台先打印1,再打印2.

image.png

任务队列

异步任务又分为:异步宏任务和异步微任务。setTimeout函数是异步宏任务,Promise对象调用的then函数是异步微任务。

异步任务队列的优先级:同一轮的异步宏任务先执行,然后再执行异步微任务。

事件循环

任务开启后,内部执行任务时可能会开启下一轮新的任务。

宏任务:脚本就是宏任务,当script标签一解析就会开始运行任务代码

当脚本运行就执行任务队列的第一轮宏任务:宏任务中第1步先执行同步任务,第2步遇到新的异步宏任务就添加到异步宏任务的任务队列中以及遇到异步微任务就添加在异步微任务队列中,第3步执行完这一轮的同步任务就执行当前异步微任务队列的任务。然后就开启下一轮的宏任务执行排在任务队列的第二轮的宏任务,第二轮的宏任务中又先执行同步任务,遇到新的异步宏任务就添加在宏任务队列中成为第三轮的宏任务和添加新的异步微任务到微任务队列中,执行完这一轮的同步任务就执行当前异步微任务队列的任务。然后又开始执行排队的宏任务,宏任务中先执行完同步任务,然后执行微任务,遇到宏任务继续排在队列中,然后执行下一轮的宏任务。如此循环就是事件循环。


            console.log(1);
            setTimeout(()=>{
                setTimeout(()=>{console.log(3);},0);第四轮宏任务
                console.log(2);
                var p2=new Promise((n1,n2)=>{
                    n1(100);
                });
                p2.then(()=>{console.log(4);});
            });//第二轮宏任务
            setTimeout(()=>{
                console.log(5);
                setTimeout(()=>{console.log(6)},0);//第五轮宏任务
                var p1=new Promise((n1,n2)=>{
                    n1(200);
                });
                console.log(9);
            });//第三轮宏任务
            p1.then(()=>{console.log(7);});
            console.log(8);

第一轮宏任务脚本任务:先执行同步任务,运行console.log(1)则控制台打印1。然后下一行是setTimeout是宏任务就添加在任务队列中作为下一轮的宏任务,然后又是setTimeout是宏任务就添加在任务队列中作为下下一轮的宏任务,在然后是p1.then是微任务,但是p1.then函数任务需要在创建p1这个promise对象时调用了第一个回调函数时才会触发then函数的执行,此时在这之前并没有创建p1这个promise对象,所以这个微任务会报错(p1 is not defined)微任务不会开启也不会添加这个微任务到任务队列,微任务后面的任务代码包括同步任务console.log(8)都不会执行,代码卡死在此处。但是之前加入到任务队列的宏任务和微任务会回到js单线程运行js回调函数的任务代码。然后开始执行第二轮的宏任务,第二轮宏任务中遇到setTimeout是宏任务就添加在任务队列中作为新的一轮的宏任务,然后执行同步任务console.log(2),控制台会打印2,然后执行同步任务创建了promise对象p2并调用了第一个回调函数,然后是微任务p2调用then函数任务,第二轮的同步任务已经执行完毕开始执行第二轮的微任务,p2.then(()=>{console.log(4);})则控制台会打印4。再执行第三轮宏任务,先运行同步任务console.log(5),控制台打印5,又遇到宏任务则添加在任务队列中,继续执行同步任务就创建了promise对象p1,再执行同步任务console.log(9),控制台会打印9,第三轮宏任务同步任务执行完毕,没有微任务。则开始执行第四轮宏任务,执行第四轮同步任务console.log(3),控制台打印3,第四轮宏任务执行完,开始执行第五轮宏任务,先执行同步任务console.log(6),控制台打印6。至此所有任务执行完成。

结果就是控制台依次打印1、报错(p1 is not defined)、2、4、5、9、3、6

image.png