Web/06-JavaScript基础:异步编程/12-事件循环机制、宏任务和微任务.md
2023-06-17 23:56:31 +08:00

268 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: 12-事件循环机制、宏任务和微任务
---
<ArticleTopAd></ArticleTopAd>
## 浏览器的事件循环机制(重要)
![image-20230608154453933](https://img.smyhvae.com/image-20230608154453933.png)
执行顺序如下:
- 同步任务:进入主线程后,立即执行。
- 异步任务:会先进入 Event Table等时间到了之后再进入 任务队列 Event Queue然后排队为什么要排队因为同一时间JS 只能执行一个任务)。比如说,`setTimeout(()=> {}, 1000)`这种定时器任务,需要等一秒之后再进入 Event Queue。
- 当主线程的任务执行完毕之后,此时主线程处于空闲状态,于是会去读取 Event Queue 中的任务队列,如果有任务,则进入到主线程去执行。
## Node.js 事件循环机制
浏览器的 EventLoop 依据的是 HTML5 规范。而 Node.js 的 EventLoop 是由Node.js底层的 libuv 规定的。 libuv是一个专注于异步IO的跨平台库。
Node.js的事件循环中有六个队列。其中微任务有两个队列宏任务有四个队列。
一、微任务队列:
- 顺序1next tick queue。比如process.nextTick
- 顺序2other queue。比如Promise的then回调、queueMicrotask
二、宏任务队列:
- 顺序3timer queue。比如setTimeout、setInterval
- 顺序4poll queue。比如IO事件
- 顺序5check queue。比如setImmediate
- 顺序6close queue。比如close事件
参考链接:
- 【荐】浏览器及nodeJS中的EventLoop事件循环机制https://www.cnblogs.com/weiyongchao/p/13766429.html
- 浏览器和Node 事件循环的区别https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/26
## 宏任务和微任务
JS中的任务分为同步任务、异步任务。
JS中的异步任务分为宏任务macrotask、微任务microtask。在早期异步任务中只有宏任务没有微任务。后来的语言标准中推出了“微任务”因为**希望微任务能够尽早执行**。
### 宏任务、微任务分类
事件循环的队列中,有两个队列。
1、**宏任务队列**,包含这些任务:
- ajax 网络请求
- setTimeout、setInterval
- DOM事件
- UI渲染
- I/O文件读写操作。
2、**微任务队列**,包含这些任务:
- Promise的then回调
- Mutation Observer API监听DOM节点变化。
- queueMicrotask():可直接将某个任务加入到微任务队列中。
在执行一个 Promise 对象时,当走完 resolve() 进入 fulfilled状态后会立刻把 `.then()`里面的代码加入到**微任务队列**当中。
### 任务的执行顺序
JS中的任务执行顺序**同步任务 --> 微任务 --> 宏任务**。
在执行任何一个宏任务之前(不是队列,是一个宏任务),都会**先查询微任务队列中是否还有任务需要执行**
- 当前宏任务执行之前,必须要保证微任务队列是空的。
- 如果微任务队列不为空,那就优先执行微任务队列中的任务。
## 任务执行顺序的面试题
实际开发中基本不会出现下面这些题目因为很多时候我们无法精准控制异步任务的执行顺序。但是它们在面试中出现的频率特别高因为熟悉这些思维训练有利于考察我们对JS单线程、事件循环机制、宏任务和微任务等原理的掌握程度。
### 题 1宏任务和微任务的执行顺序
```js
setTimeout(() => {
// 宏任务
console.log('setTimeout');
}, 0);
new Promise((resolve, reject) => {
resolve();
console.log('promise1'); // 同步任务
}).then((res) => {
// 微任务
console.log('promise then');
});
console.log('同步任务'); // 同步任务
```
打印结果:
```
promise1
同步任务
promise then
setTimeout
```
上方代码执行的顺序依次是:**同步任务 --> 微任务 --> 宏任务**。
### 题 2在宏任务中嵌套了微任务
```js
new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
console.log('setTimeout'); // 宏任务
}, 0);
console.log('promise1');
}).then((res) => {
// 微任务
console.log('promise then');
});
console.log('同步任务');
```
打印结果:
```
promise1
同步任务
setTimeout
promise then
```
上方代码解释:在执行宏任务的**过程中**,创建了一个微任务。但是需要**先把当前这个宏任务执行完**,再去**创建并执行**微任务。
### 题3综合题
```js
console.log("script start")
setTimeout(() => {
console.log("setTimeout1");
new Promise(resolve => {
resolve();
}).then(() => {
new Promise(resolve => {
resolve();
}).then(() => {
console.log("then1");
});
console.log("then2");
});
});
new Promise(resolve => {
// 下面这两行代码,即便调换顺序,也不影响打印结果
console.log("promise1");
resolve();
}).then(() => {
console.log("then3");
});
setTimeout(() => {
console.log("setTimeout2");
});
console.log('同步代码');
queueMicrotask(() => {
console.log("queueMicrotask")
});
new Promise(resolve => {
resolve();
}).then(() => {
console.log("then4");
});
console.log("script end");
```
打印结果:
```
// 第一次循环
script start
promise1
同步代码
script end
// 第二次循环
then3
queueMicrotask
then4
// 第三次循环
setTimeout1
then2
then1
// 第四次循环
setTimeout2
```
### 题4async await 题目
代码举例:
```js
console.log('script start')
async function async2() {
console.log('async2')
}
async function async1() {
console.log('async1 start')
await async2();
console.log('async1 end')
}
setTimeout(() => {
console.log('setTimeout')
}, 0)
async1();
new Promise(resolve => {
console.log('promise1')
resolve();
}).then(function () {
console.log('then1')
})
console.log('script end');
```
打印结果:
```
script start
async1 start
async2
promise1
script end
async1 end
then1
setTimeout
```
## 赞赏作者
创作不易,你的赞赏和认可,是我更新的最大动力:
![](https://img.smyhvae.com/20220401_1800.jpg)