浏览器中的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

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


总结

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

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

JV_32

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

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

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

打赏作者

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

抵扣说明:

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

余额充值