浏览器事件循环探讨 event loop

本文深入探讨JavaScript中宏任务与微任务的区别及执行机制,通过具体示例解释它们如何影响代码执行顺序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有以下demo,最终控制台会输入的内容是啥

 setTimeout(()=>{

            console.log("timer1")

            Promise.resolve(1).then(()=>{

                console.log("promise1")

            })

            console.log("timer 1normal")

        })

        setTimeout(()=>{

            console.log("timer2")

        })

        console.log("primary")

  • 1,第一个是primary,这个没有疑问,住流程的代码先执行嘛
  • 2,然后是timer1
  • 3,再然后是timer 1normal,
  • 4,,接着是promise1
  • 5,timer2

为什么1比2快呢?

因为1是主流程代码,2是宏任务里面的代码啊🤔

那为什么3比4快呢?

因为3是主流程代码啊,4是微任务里面的代码🤔

好像这样开始有点迷茫了~~,3和4不是都在宏任务里面的么。

宏任务队列和微任务队列

得有【宏任务队列和微任务队列】这些概念才能更好理解代码

假设浏览器底层有以下代码

var macroTask = [];//宏任务

        var microTask = [];//微任务

        var timer;//事件轮训的定时器

        window.setTimeoutx = function (cb) {

            macroTask.push(cb);

        }

        //模拟Promise.resolve(1).then

        window.micpush = function (cb) {

            microTask.push(cb);

        }

        var eventLoop = function () {

            clearTimeout(timer);

            //这块代码是为了演示作用,正常环境不会有==========【

            if (macroTask.length || microTask.length) {

                console.warn("macroTask", macroTask, 'microTask', microTask)

            }

            else {

                console.warn("宏任务和微任务队列为空")



            }

            //这块代码是为了演示作用,正常环境不会有==========】

            function callListFn(arr, ismac) {

                let curr = arr.shift();

                curr();

                //再检测下是否有微任务

                //这里其实这样写不啊合适,只是暂时配合模拟效果

                if (ismac) {

                    //微任务

                    while (microTask.length) {

                        callListFn(microTask);

                    }



                }

            }



            //微任务

            while (microTask.length) {

                callListFn(microTask);

            }



            //宏任务

            while (macroTask.length) {

                callListFn(macroTask, true);

            }

            //模拟事件循环,定时轮询

            timer = setTimeout(() => {

                eventLoop();

            }, 1000)

        };//事件循环



        //开启事件循环

        eventLoop();

我们开发时候所写的代码

  • setTimeoutx模拟window.setTimeout
  • micpush 模拟Promise.resolve(1).then 这个api

      

 setTimeoutx(()=>{

            console.log("timer1")

             //模拟Promise.resolve(1).then 这个api

            micpush(()=>{

                console.log("promise1")

            })

            console.log("timer 1normal")

        })

        setTimeoutx(()=>{

            console.log("timer2")

        })

        console.log("primary")

        console.warn("macroTask",macroTask,'microTask',microTask)

你会发现浏览器console.log打印的内容和我们第一段代码一模一样

本质

        setTimeout,Promise.resolve(1).then他们都是往底层的任务队列里面添加个函数元素而已,并非任务本身,函数类型的参数才是要被执行的任务,只是执行的权限交给了事件轮询。而事件轮询是定期的遍历执行微任务队列和宏任务队列里面的元素。只是微任务的执行优先于宏任务。

步骤解说

注意序号❕

//1
     setTimeout(()=>{

       //4

         console.log("timer1")

        // 5

         Promise.resolve(1).then(()=>{

               //8 console.log("promise1")

            })

//6

Promise.resolve(1).then(()=>{

               //9 console.log("promise1")

            })

//7

            console.log("timer 1normal")

        })

//2.

        setTimeout(()=>{

           //10  console.log("timer2")

        })

// 3

  console.log("primary")

针对上面不同步骤的微任务队列和 任务队列

//1

macroTask = [fn1];//宏任务

microTask = [];//微任务

//2

macroTask = [fn1,fn2];//宏任务

microTask = [];//微任务

//3

macroTask = [fn1,fn2];//宏任务

microTask = [];//微任务

执行【打印"primary”】

【开始事件轮询1】

//4

执行【打印"timer1”】

macroTask = [fn2];//宏任务,正在执行fn1宏任务,所以要把他移除处队列

microTask = [];//微任务

//5

执行【打印"timer1”】—已经执行了,

macroTask = [fn2];//宏任务,正在执行fn1宏任务,所以要把他移除处队列

microTask = [fn3];//微任务,Promise.resolve(1).then 给微任务队列塞入个元素

//6

执行【打印"timer1”】—已经执行了,

macroTask = [fn2];//宏任务,正在执行fn1宏任务,所以要把他移除处队列

microTask = [fn3,fn4];//微任务,Promise.resolve(1).then 给微任务队列塞入个元素

//7

执行【打印"timer 1normal”】

macroTask = [fn2];//宏任务,没有执行下一个事件轮训所以【本轮事件轮训】宏任务还有一个元素

microTask = [fn3,fn4];//微任务,因为还没有执行下一个事件轮训所以【本轮事件轮训】微任务还有2个元素

//开启第二轮事件轮询,8,9,10,微任务【8,9】优先

最终

microTask = [];//微任务

macroTask = [];//宏任务

以上的代码可以在线测试

疑问?

上面【浏览器底层代码】它不会卡住js的运行么,如果是个死循环的话

答案:

不会,浏览器是多线程的,可以开一个独立的线程去执行这个死循环,和我们的网页渲染,js执行的线程是完全分开的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陆康永

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值