浅谈Event Loop


讲Event Loop之前,先说一下JS的特点,Javascript是一个单线程非阻塞(只有一个主线程,也就是它一次只能执行一段代码)、异步、解释性脚本语言。如果遇到耗时特别长的任务,网页就短时间不会有响应,造成一种“假死”的现象。

你也许会问,JavaScript为什么是单线程,难道不能实现为多线程吗?

这跟历史有关系。JavaScript从诞生起就是单线程。原因大概是不想让浏览器变得太复杂,因为多线程需要共享资源、且有可能修改彼此的运行结果,对于一种网页脚本语言来说,这就太复杂了。后来就约定俗成,JavaScript为一种单线程语言。

Event Loop是什么?
Event Loop即事件循环,指浏览器解决javaScript单线程运行时不会阻塞的一种机制,也就是我们提到的“异步”。

宏队列和微队列
宏队列:macrotask。 一些异步任务的回调会依次进入macro task queue,等待后续被调用,这些异步任务包括:

setTimeout
setInterval
setImmediate (Node独有)
requestAnimationFrame (浏览器独有)
I/O
UI rendering (浏览器独有)
微队列:

微队列,microtask。 另一些异步任务的回调会依次进入micro task queue,等待后续被调用,这些异步任务包括:

process.nextTick (Node独有)
Promise
Object.observe
MutationObserver
我们首先看一张图,这张图大致内容就是介绍宏队列和微队列的循环机制:

1.首先执行程序的同步代码,然后这些同步代码中有些是同步语句,有些是异步语句。

2.程序代码执行完毕后,调用栈Stack会清空;

3.从微队列microtask queue中取出位于队首的回调任务,放入调用栈Stack中执行,执行完后microtask queue长度减1;以此类推,直到微队列结束。

4.microtask queue中的所有任务都执行完毕,此时microtask queue为空队列,调用栈Stack也为空;

5.取出宏队列macrotask queue中位于队首的任务,放入Stack中执行;直到红队列结束,执行完毕后,调用栈Stack为空。

以上就是浏览器的Event Loop事件循环

文字讲解可能有些不太好理解。下面我们用一段代码讲讲具体过程吧:

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

接下来我们分析一下具体的过程吧:

Step 1

console.log(1)

Stack Queue: [console]

Macrotask Queue: []

Microtask Queue: []

打印结果:
1

Step 2

setTimeout(() => {
  // 这个回调函数叫做callback1,setTimeout属于macrotask,所以放到macrotask queue中
  console.log(2);
  Promise.resolve().then(() => {
    console.log(3)
  });
});

Stack Queue: [setTimeout]

Macrotask Queue: [callback1]

Microtask Queue: []

打印结果:
1

Step 3

new Promise((resolve, reject) => {
  // Promise新建后立即执行,所以会先输出4
  console.log(4)
  resolve(5)
}).then((data) => {
  // 这个回调函数叫做callback2,promise属于microtask,所以放到microtask queue中
  console.log(data);
})

Stack Queue: [promise]

Macrotask Queue: [callback1]

Microtask Queue: [callback2]

打印结果:
1
4

Step 5

setTimeout(() => {
  // 这个回调函数叫做callback3,setTimeout属于macrotask,所以放到macrotask queue中
  console.log(6);
})

Stack Queue: [setTimeout]

Macrotask Queue: [callback1, callback3]

Microtask Queue: [callback2]

打印结果:
1
4

Step 6

console.log(7)

Stack Queue: [console]

Macrotask Queue: [callback1, callback3]

Microtask Queue: [callback2]

打印结果:
1
4
7

好啦,全局Script代码执行完了,进入下一个步骤,从microtask queue中依次取出任务执行,直到microtask queue队列为空。
Step 7

console.log(data) // 这里data是Promise的返回值5

Stack Queue: [callback2]

Macrotask Queue: [callback1, callback3]

Microtask Queue: []

打印结果:
1
4
7
5

这里microtask queue中只有一个任务,执行完后开始从宏任务队列macrotask queue中取位于队首的任务执行
Step 8

console.log(2)

Stack Queue: [callback1]

Macrotask Queue: [callback3]

Microtask Queue: []

打印结果:
1
4
7
5
2

但是,执行callback1的时候又遇到了另一个Promise,Promise异步执行完后在microtask queue中又注册了一个callback4回调函数

Step 9

Promise.resolve().then(() => {
  // 这个回调函数叫做callback4,promise属于microtask,所以放到microtask queue中
  console.log(3)
});

Stack Queue: [promise]

Macrotask v: [callback3]

Microtask Queue: [callback4]

打印结果:
1
4
7
5
2

取出一个宏任务macrotask执行完毕,然后再去微任务队列microtask queue中依次取出执行
Step 10

console.log(3)

Stack Queue: [callback4]

Macrotask Queue: [callback3]

Microtask Queue: []

打印结果:
1
4
7
5
2
3

微任务队列全部执行完,再去宏任务队列中取第一个任务执行
Step 11

console.log(6)

Stack Queue: [callback3]

Macrotask Queue: []

Microtask Queue: []

打印结果:
1
4
7
5
2
3
6

以上,全部执行完后,Stack Queue为空,Macrotask Queue为空,Micro Queue为空
Stack Queue: []

Macrotask Queue: []

Microtask Queue: []

最终打印结果:
1
4
7
5
2
3
6
以上内容大部分采纳 liuxuan: 链接.,内容有部分改动,学完Event Loop,可以增加了知识的深度,更好的理解Event Loop机制,从容应对面试题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值