• 浏览器中的Event Loop



    一、Event Loop是什么?

      JavaScript是一门单线程语言,它的异步和多线程的实现,是通过Event Loop(也称为事件循环)来实现的。Event Loop是一个执行机制,浏览器和Node.js基于各种不同的技术实现了各自的EventLoop。本文重点来讲述基于浏览器的Event Loop。

    二、调用栈

      咱都知道JavaScript分为同步任务异步任务。Event Loop刚开始时会将同步任务一行一行执行,遇到函数调用,会把它压入调用栈(call stack)中,当函数执行完毕后会从栈中弹出,根据栈后进先出的顺序执行同步代码。
    举个栗子:

    function func1() {
        console.log(1);
    }
    function func2() {
        console.log(2);
        func1();
        console.log(3);
    }
    func2();
    '
    运行

    Step1:

    func2()压入调用栈中并执行:call stack[ func2(); ]

    Step2: console.log(2);
    Step3:

    func1()压入调用栈中并执行:call stack[ func1(); func2(); ]

    Step4: console.log(1);
    Step5:

    func1()执行完毕并弹出调用栈:call stack[ func2(); ]

    Step6: console.log(3);
    Step7:

    func2()执行完毕并弹出调用栈:call stack[ ]

    输出顺序:2 1 3

    三、任务队列

      任务队列可以看作是JavaScript另开辟的另一个副线程,JavaScript中的常见的异步任务如:setTimeout、setInterval、I/O操作、DOM事件、Promise的回调(then、catch、finally…)等,其回调函数会入队到任务队列中,等待同步任务的执行完毕,也就是调用栈为空的时候,再去依次执行异步任务。按照队列先进先出的的顺序执行回调。
      而任务队列又可分为宏任务队列与微任务队列,在任务队列中,Event Loop的执行顺序为:

    1.从微任务队列(microtask queue)中取出位于队首的任务,放入调用栈执行,微任务队列长度减1
    2.不断执行1的操作(如执行过程中产生微任务将继续放入队尾等待执行),以此类推,直到微任务队列为空
    3.微任务队列执行完毕,此时microtask queue为空队列,调用栈call stack也为空
    4.取出宏任务队列(macrotask queue)的队首任务,放入调用栈执行,宏任务队列长度减1
    5.不断执行4的操作,如执行过程中产生微任务将放入microtask queue队列并开始掉头执行微任务
    6.执行1、2操作直到微任务队列再次为空,继续执行宏任务
    7.直到所有任务执行完毕,调用栈call stack、微任务队列microtask queue、宏任务队列macrotask queue都为空

    1.宏任务队列

      常见的宏任务如:setTimeout、setInterval、I/O操作
    举个栗子:

    function func1() {
        console.log(1);
    }
    function func2() {
        setTimeout(() => {
            console.log(2);
        }, 0);
        func1();
        console.log(3);
    }
    func2();
    '
    运行

    Step1:

    func2()压入调用栈中并执行:call stack[ func2(); ]

    Step2:

    setTimeout()压入栈中,其内部回调函数callback进入宏任务队列,并弹出定时器setTimeout()
    call stack[ setTimeout(); func2(); ]
    macrotask queue[ callback ]

    Step3:

    func1()压入调用栈中并执行:
    call stack[ func1(); func2(); ] **
    macrotask queue[ callback ]

    Step4: console.log(1);

    Step5:

    func1()执行完毕并弹出调用栈:
    call stack[ func2(); ]
    macrotask queue[ callback ]

    Step6: console.log(3);

    Step7:

    func2()执行完毕并弹出调用栈:
    call stack[ ]
    macrotask queue[ callback ]

    Step8:

    此时调用栈为空,因无微任务要执行,从宏任务队列中取出回调函数入调用栈执行: console.log(2);
    call stack[ ]
    macrotask queue[ ]

    输出顺序:1 3 2

    2.微任务队列

      常见的微任务如:promise的回调(then、catch、finally…)
    举个栗子:

    function func1() {
        console.log(1);
    }
    function func2() {
        new Promise((resolve, reject) => {
            resolve(4);
            console.log(5);
        }).then(data => {
            console.log(2);
        })
        func1();
        console.log(3);
    }
    func2();
    '
    运行

    Step1:

    func2()压入调用栈中并执行:call stack[ func2(); ]

    Step2:

    new Promise压入栈中并执行其内部同步代码:
    call stack[ new Promise; func2(); ]

    Step3:

    Promise内部同步代码resolve(4)、console.log(5)依次压入栈中并执行:console.log(5)
    call stack[ new Promise; func2(); ]

    Step4:

    promise.then中的回调函数callback进入微任务队列,并弹出new Promise构造函数:
    call stack[ func2(); ]
    microtask queue[ callback ]

    Step5:

    func1()压入调用栈中并执行:
    call stack[ func1(); func2(); ] **
    microtask queue[ callback ]

    Step4: console.log(1);

    Step5:

    func1()执行完毕并弹出调用栈:
    call stack[ func2(); ]
    microtask queue[ callback ]

    Step6: console.log(3);

    Step7:

    func2()执行完毕并弹出调用栈:
    call stack[ ]
    microtask queue[ callback ]

    Step8:

    此时调用栈为空,从微任务队列中取出回调函数进入调用栈执行: console.log(2);
    call stack[ ]
    microtask queue[ ]

    输出顺序:5 1 3 2

    四、案例分析

      由同步与异步任务的执行顺序描述以及以上两例分析,相信已经对同步任务和异步任务的执行顺序有了基本的一定的熟悉,下面将结合同步与异步任务(结合宏任务与微任务),根据上文执行顺序的描述,来练习一下叭!

    console.log(1);
    
    setTimeout(() => {
      console.log(2);
      Promise.resolve().then(() => {
        console.log(3)
      });
    });
    
    new Promise((resolve, reject) => {
      console.log(4)
      resolve(5)
    }).then((data) => {
      console.log(data);
    })
    
    setTimeout(() => {
      console.log(6);
    })
    
    console.log(7);
    '
    运行

    输出顺序:1 4 7 5 2 3 6

    结合先同后异,先微后宏,宏中带微掉头微的顺序分析,你做对了吗~~


    总结

      希望大家看了本篇文章都有收获 …有不解的地方评论区秒答复,简单阐述了一下该知识点,有错误的话,望斧正!!

  • 相关阅读:
    【Python机器学习】sklearn.datasets回归任务数据集
    什么是云存储,从对象存储说起?
    Spring Boot集成微信支付JSAPIV3保姆教程
    如何在Excel中自动创建报告 Excel中自动创建报告的方法
    Android使用Banner框架实现轮播图
    Ventoy系统启动盘制作
    游戏服务器成DDoS最大攻击重灾区
    【数学建模】单、多因素试验的方差分析(Matlab代码实现)
    GaussDB(for Redis)双活容灾支持4大应用场景,为业务安全保驾护航
    Python数据的输入与输出
  • 原文地址:https://blog.csdn.net/m0_53375764/article/details/127093814