***
Event Loop 这个概念相信大家或多或少都了解过,所谓温故而知新,so,今天,我们就从event loop出发,看看在事件的执行过程中,他都经历了些什么。
#### 什么是event loop
event loop是js的事件执行机制,我们一般简称为事件循环(之所以称作事件循环,是因为它经常被用于类似如下的方式来实现)
```javascript
while (queue.waitForMessage()) {
queue.processNextMessage();
}
```
如果当前没有任何消息`queue.waitForMessage` 会等待同步消息到达,当完成当前任务后,继续去查看有无需要执行的任务如果需要执行,就再次执行,如此循环,所以称之为事件循环。
#### 同步和异步任务
要了解异步线程我们首先应该明白它的用处,因为js的`单线程`特性,任务的执行顺序都是依次执行,而当我们在工作中遇到网络请求,前后端交互的时候,你的数据不会马上拿到,这需要时间,如果等拿到数据再执行下面的代码,这样如果多次请求就会发现加载速度极慢,这样显然不合理,这样就会出现很多次的暂停等待,所以这时候 需要执行异步任务,当我们发起请求时候,采用异步的方式,浏览器检测到其为异步时,就会开辟一个新的进程处理该函数,然后继续执行后面的任务,当完成了执行栈里的同步任务之后,再检测是否有异步任务需要执行,最后执行异步任务。
```javascript
-同步任务进入主线程,按顺序从上而下依次执行,
-异步任务,进入`event table` ,注册回调函数 `callback` ,
任务完成后,将`callback`移入`event queue`中等待主线程调用
```
#### 异步任务分为微任务和宏任务
在执行过程中,我们知道了同步任务会优先异步任务执行,那么在异步中呢,异步中同样包含微任务和宏任务,首先我们大概了解下微任务和宏任务,在js中:
* 微任务(micor Task) :promise MutationObserver process.nextTick
* 宏任务(macro Task):script settimeout setinterval setImmediate requestAnimationFrame
> 宏任务
>
> | # | 浏览器 | node |
> | --------------------- | :----: | :--: |
> | I/O | √ | √ |
> | setTimeOut | √ | √ |
> | setInterval | √ | √ |
> | setImmediate | × | √ |
> | requestAnimationFrame | √ | × |
>
> 微任务
>
> | # | 浏览器 | node |
> | -------------------------- | :----: | :--: |
> | process.nextTick | × | √ |
> | MutationObserver | √ | × |
> | Promise.then catch finally | √ | √ |
这两种任务在不同环境下支持的各不同,今天我们主要看看在浏览器中,我们经常会遇到的有 `promise` 和 `setTimeout` 我们通过下面这段代码来看看:
```javascript
console.log(1)
setTimeout(() => console.log(2), 0)
new Promise((resolve, reject) => {
console.log(3)
resolve()
}).then(() => {
console.log(4)
})
```
首先来分析下,这段代码中包含同步任务,包含异步的宏任务`setTimeout`,包含异步的微任务`promise`,这套题的答案是1.3.4.2 ,我们首先找到同步任务,1 3 是同步任务,然后执行异步任务,异步任务如果按顺序执行则是24 但是答案是4.2那么我们可以知道 promise的执行顺序优先于setTimeout所以由此可知,在异步任务中,微任务优先于宏任务执行,可以看看下图。
`红线就是任务的执行顺序`
`黑线是任务的结构`
看完这么多下面来完成下面这道题并加以分析:
```javascript
console.log(1)
setTimeout(() => {
console.log(2)
new Promise((resolve, reject) => {
console.log(3)
resolve()
}).then(() => {
console.log(4)
})
}, 0)
new Promise((resolve, reject) => {
console.log(5)
resolve()
}).then(() => {
console.log(6)
})
setTimeout(() => {
console.log(7)
new Promise((resolve, reject) => {
console.log(8)
resolve()
}).then(() => {
console.log(9)
})
}, 0)
console.log(10)
const promise = new Promise((resolve, reject) => {
console.log(1);
console.log(2);
});
promise.then(() => {
console.log(3);
});
console.log(4);
```
> 答案:`1 , 5 , 10 , 6 , 2 , 3 , 4 , 7 , 8 , 9`
废话不多说直接解题
+ 进入主线程开始执行, 遇到 `console.log(1)`, 输出 `1`
+ 遇到一个 `setTimeout` 宏任务, 将其回调函数推入 `macro Task` 的 `event queue` 中,`macro Task` 的 `event queue` 中记一个任务 `setTimeout1`
+ 然后碰到 `promise` 微任务, 直接执行 `new Promise` 输出 `5`, 并将 `then` 函数的回调函数推入 `micro Task` 的 `event queue` 中, `micro Task` 的 `event queue` 中记 一个 微任务 `promise1`
+ 又遇到了 `setTimeout` 宏任务, 同理,将其回调函数推入 `macro Task` 的 `event queue` 中,`macro Task` 的 `event queue` 中记一个任务 `setTimeout2`
+ 最后,执行 `console.log(10)`, 输出 10
> 上一轮事件循环结束,我们发现,已经输出 `1 5 10` 了, 按照我们之前所说,这个时候,主线程会去检查 是否存在微任务,不难发现,这个时候的 `event queue` 是这个样子的
| **micro Task (微任务)** | **macro Task(宏任务)** |
| :---------------------: | :--------------------: |
| promise1 | setTimeout1 |
| | setTimeout2 |
```javascript
主线程 ---> promis1 ---> settimeout1 ---> settimeou2 ---> 循环检查主线程任务栈是否还有任务
```
#### 结语