***
Event Loop 這個(gè)概念相信大家或多或少都了解過,所謂溫故而知新丐吓,so,今天,我們就從event loop出發(fā),看看在事件的執(zhí)行過程中,他都經(jīng)歷了些什么液肌。
#### 什么是event loop
event loop是js的事件執(zhí)行機(jī)制,我們一般簡稱為事件循環(huán)(之所以稱作事件循環(huán)鸥滨,是因?yàn)樗?jīng)常被用于類似如下的方式來實(shí)現(xiàn))
```javascript
while (queue.waitForMessage()) {
? ? ? ? ? ? queue.processNextMessage();
? ? ? ? ? ? }
```
? ? ? 如果當(dāng)前沒有任何消息`queue.waitForMessage` 會(huì)等待同步消息到達(dá),當(dāng)完成當(dāng)前任務(wù)后嗦哆,繼續(xù)去查看有無需要執(zhí)行的任務(wù)如果需要執(zhí)行谤祖,就再次執(zhí)行,如此循環(huán)老速,所以稱之為事件循環(huán)粥喜。
#### 同步和異步任務(wù)
? ? ? 要了解異步線程我們首先應(yīng)該明白它的用處,因?yàn)閖s的`單線程`特性橘券,任務(wù)的執(zhí)行順序都是依次執(zhí)行额湘,而當(dāng)我們在工作中遇到網(wǎng)絡(luò)請求,前后端交互的時(shí)候旁舰,你的數(shù)據(jù)不會(huì)馬上拿到锋华,這需要時(shí)間,如果等拿到數(shù)據(jù)再執(zhí)行下面的代碼箭窜,這樣如果多次請求就會(huì)發(fā)現(xiàn)加載速度極慢毯焕,這樣顯然不合理,這樣就會(huì)出現(xiàn)很多次的暫停等待磺樱,所以這時(shí)候 需要執(zhí)行異步任務(wù)纳猫,當(dāng)我們發(fā)起請求時(shí)候,采用異步的方式坊罢,瀏覽器檢測到其為異步時(shí)续担,就會(huì)開辟一個(gè)新的進(jìn)程處理該函數(shù)擅耽,然后繼續(xù)執(zhí)行后面的任務(wù)活孩,當(dāng)完成了執(zhí)行棧里的同步任務(wù)之后,再檢測是否有異步任務(wù)需要執(zhí)行乖仇,最后執(zhí)行異步任務(wù)憾儒。
```javascript
-同步任務(wù)進(jìn)入主線程,按順序從上而下依次執(zhí)行乃沙,
-異步任務(wù)起趾,進(jìn)入`event table` ,注冊回調(diào)函數(shù) `callback` 警儒,
任務(wù)完成后训裆,將`callback`移入`event queue`中等待主線程調(diào)用
```
#### 異步任務(wù)分為微任務(wù)和宏任務(wù)
? ? ? 在執(zhí)行過程中,我們知道了同步任務(wù)會(huì)優(yōu)先異步任務(wù)執(zhí)行蜀铲,那么在異步中呢边琉,異步中同樣包含微任務(wù)和宏任務(wù),首先我們大概了解下微任務(wù)和宏任務(wù)记劝,在js中:
* 微任務(wù)(micor Task) :promise? MutationObserver process.nextTick
* 宏任務(wù)(macro Task):script settimeout setinterval setImmediate requestAnimationFrame
> 宏任務(wù)
>
> | #? ? ? ? ? ? ? ? ? ? | 瀏覽器 | node |
> | --------------------- | :----: | :--: |
> | I/O? ? ? ? ? ? ? ? ? |? √? ? |? √? |
> | setTimeOut? ? ? ? ? ? |? √? ? |? √? |
> | setInterval? ? ? ? ? |? √? ? |? √? |
> | setImmediate? ? ? ? ? |? ×? ? |? √? |
> | requestAnimationFrame |? √? ? |? ×? |
>
> 微任務(wù)
>
> | #? ? ? ? ? ? ? ? ? ? ? ? ? | 瀏覽器 | node |
> | -------------------------- | :----: | :--: |
> | process.nextTick? ? ? ? ? |? ×? ? |? √? |
> | MutationObserver? ? ? ? ? |? √? ? |? ×? |
> | Promise.then catch finally |? √? ? |? √? |
這兩種任務(wù)在不同環(huán)境下支持的各不同变姨,今天我們主要看看在瀏覽器中,我們經(jīng)常會(huì)遇到的有 `promise` 和 `setTimeout` 我們通過下面這段代碼來看看:
```javascript
? ? console.log(1)
? ? setTimeout(() => console.log(2), 0)
? ? new Promise((resolve, reject) => {
? ? ? ? console.log(3)
? ? ? ? resolve()
? ? }).then(() => {
? ? ? ? console.log(4)
? ? })
```
首先來分析下厌丑,這段代碼中包含同步任務(wù)定欧,包含異步的宏任務(wù)`setTimeout`,包含異步的微任務(wù)`promise`,這套題的答案是1.3.4.2 渔呵,我們首先找到同步任務(wù),1 3 是同步任務(wù)砍鸠,然后執(zhí)行異步任務(wù)扩氢,異步任務(wù)如果按順序執(zhí)行則是24? 但是答案是4.2那么我們可以知道 promise的執(zhí)行順序優(yōu)先于setTimeout所以由此可知,在異步任務(wù)中爷辱,微任務(wù)優(yōu)先于宏任務(wù)執(zhí)行,可以看看下圖类茂。
`紅線就是任務(wù)的執(zhí)行順序`
`黑線是任務(wù)的結(jié)構(gòu)`
看完這么多下面來完成下面這道題并加以分析:
```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`
? 廢話不多說直接解題
+ 進(jìn)入主線程開始執(zhí)行, 遇到 `console.log(1)`托嚣, 輸出 `1`
+? 遇到一個(gè) `setTimeout` 宏任務(wù)巩检, 將其回調(diào)函數(shù)推入 `macro Task` 的 `event queue` 中,`macro Task` 的 `event queue` 中記一個(gè)任務(wù) `setTimeout1`
+ 然后碰到 `promise` 微任務(wù)示启, 直接執(zhí)行 `new Promise` 輸出 `5`, 并將 `then` 函數(shù)的回調(diào)函數(shù)推入 `micro Task` 的 `event queue` 中, `micro Task` 的 `event queue` 中記 一個(gè) 微任務(wù) `promise1`
+ 又遇到了 `setTimeout` 宏任務(wù)兢哭, 同理,將其回調(diào)函數(shù)推入 `macro Task` 的 `event queue` 中夫嗓,`macro Task` 的 `event queue` 中記一個(gè)任務(wù) `setTimeout2`
+ 最后迟螺,執(zhí)行 `console.log(10)`, 輸出 10
> 上一輪事件循環(huán)結(jié)束,我們發(fā)現(xiàn)舍咖,已經(jīng)輸出 `1 5 10` 了矩父, 按照我們之前所說,這個(gè)時(shí)候排霉,主線程會(huì)去檢查 是否存在微任務(wù)窍株,不難發(fā)現(xiàn),這個(gè)時(shí)候的 `event queue` 是這個(gè)樣子的
| **micro Task (微任務(wù))** | **macro Task(宏任務(wù))** |
| :---------------------: | :--------------------: |
|? ? ? ? promise1? ? ? ? |? ? ? setTimeout1? ? ? |
|? ? ? ? ? ? ? ? ? ? ? ? |? ? ? setTimeout2? ? ? |
```javascript
主線程 ---> promis1 ---> settimeout1 ---> settimeou2 ---> 循環(huán)檢查主線程任務(wù)棧是否還有任務(wù)
```
#### 結(jié)語