淺談event loop

由一道面試題引出:javascript

//請寫出輸出內容
async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
	console.log('async2');
}

console.log('script start');

setTimeout(function() {
    console.log('setTimeout');
}, 0)

async1();

new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');


/* script start async1 start async2 promise1 script end async1 end promise2 setTimeout */複製代碼

知識點:

在理清楚如下幾個知識點後上面問題就迎刃而解了:java

1. 宏任務:包括script(總體代碼),setTimeout,setInterval,I/O,UI交互,postMessagegit

2. 微任務:包括promise.then,MutationObservergithub

3. 執行過程: a) 執行一個宏任務 b)過程當中遇到微任務,則添加其到微任務隊列 c)當前宏任務執行完以後,開始依次執行全部微任務 d)GUI線程接管開始渲染 e)渲染完成交給js線程重複這個過程面試

4. async標誌的函數裏面,只有當await的表達式返回結果後,await之後的代碼纔會被執行promise

5. async/await實際上是promise的語法糖,因此await之後的代碼至關於放入了then裏面瀏覽器

解題:

1. 代碼開始,宏任務是scriptbash

Macro tasks
---------------
|  script     |
---------------複製代碼

2. 從上往下,首先看到了同步代碼log,則輸出 script startmarkdown

3. 遇到settimeout,將其裏面的內容加入宏任務隊列async

Macro tasks
----------------------------------
|  script  |   log 'setTimeout'  |
----------------------------------複製代碼

4. 運行async1函數,沒遇到await以前都是同步代碼,輸出 async1 start

5. 遇到await,解析成以下形式

Promise.resolve(async2()).then(() => {console.log('async1 end')})複製代碼

6. async2由於直接返回值無需等待,因此輸出 async2

7. 如上改寫能夠看出那句log被放入了微任務隊列

Macro tasks queue
----------------------------------
|  script  |   log 'setTimeout'  |
----------------------------------
Micro tasks queue
-----------------------
|  log 'async1 end'   |
-----------------------複製代碼

8. 遇到new promise實例化過程,此過程依然是同步的,輸出promise1,並把then的內容加入微隊列

Macro tasks queue
----------------------------------
|  script  |   log 'setTimeout'  |
----------------------------------
Micro tasks queue
-------------------------------------------
|  log 'async1 end'   |  log 'promise2'   |
-------------------------------------------複製代碼

9. 輸出同步log:script end

10. 此時已經到了script這個宏任務的最後了,開始執行其全部微任務,清空微任務隊列,輸出async1 endpromise2

Macro tasks queue
----------------------------------
|  script  |   log 'setTimeout'  |
----------------------------------
Micro tasks queue
-------------------------------------------
|                                         |
-------------------------------------------複製代碼

11. script宏作完,本身出隊列

Macro tasks queue
----------------------------------
|       log 'setTimeout'         |
----------------------------------
Micro tasks queue
-------------------------------------------
|                                         |
-------------------------------------------複製代碼

12. 瀏覽器開始渲染,渲染完成開始下一個宏任務

13. 輸出setTimeout, 清空宏隊列,完成!

改版題:

這種題就算知道了解法,也推薦畫圖,否則很容易暈

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
    //async2作出以下更改:
    new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
    });
}
console.log('script start');

setTimeout(function() {
    console.log('setTimeout');
}, 0)
async1();

new Promise(function(resolve) {
    console.log('promise3');
    resolve();
}).then(function() {
    console.log('promise4');
});

console.log('script end');複製代碼
// 答案
script start
async1 start
promise1
promise3
script end
promise2
async1 end
promise4
setTimeout複製代碼
settimeout加入宏隊列,promise1在實例化時輸出,promise2和async1 end加入微隊列
Macro tasks queue                               Micro tasks queue
---------------------------                 ------------------------------
|  script   |  setTimeout  |                |   promise2  |  async1 end  |
---------------------------                 ------------------------------
promise3輸出,promise4加入微隊列
-------------------------                   ---------------------------------------
|  script |  settimeout  |                  | promise2  | async1 end  |  promise4  |
-------------------------                   ---------------------------------------複製代碼

另外一個

async function async1() {
    console.log('async1 start');
    await async2();
    //更改以下:
    setTimeout(function() {
        console.log('setTimeout1')
    },0)
}
async function async2() {
    //更改以下:
	setTimeout(function() {
		console.log('setTimeout2')
	},0)
}
console.log('script start');

setTimeout(function() {
    console.log('setTimeout3');
}, 0)
async1();

new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');複製代碼
// 答案
script start
async1 start
promise1
script end
promise2
setTimeout3
setTimeout2
setTimeout1複製代碼
settimeout3加入宏隊列,settimeout2也加入宏,注意此時後面總體代碼加入微隊列
Macro tasks queue                               Micro tasks queue
-------------------------------------------      ------------------------------
|  script   |  setTimeout3  |  settimeout2 |     |  setTiemout(settimout1)   |
-------------------------------------------      ------------------------------
promise3輸出,promise4加入微隊列
-----------------------------------------       -----------------------------------------
|  script |  settimeout3  |  settimeout2 |      | setTimeout(settimeout1) |  promise2  |
-----------------------------------------       -----------------------------------------
script end輸出後開始執行微隊列,setTimoue被取出執行,把settimeout1加入宏隊列
--------------------------------------------------      --------------
| script | setimeout3 | settimeou2 | settimeout1 |      |  promise2  |
--------------------------------------------------       -------------複製代碼

另外一個

async function a1 () {
    console.log('a1 start')
    await a2()
    console.log('a1 end')
}
async function a2 () {
    console.log('a2')
}

console.log('script start')

setTimeout(() => {
    console.log('setTimeout')
}, 0)

Promise.resolve().then(() => {
    console.log('promise1')
})

a1()

let promise2 = new Promise((resolve) => {
    resolve('promise2.then')
    console.log('promise2')
})

promise2.then((res) => {
    console.log(res)
    Promise.resolve().then(() => {
        console.log('promise3')
    })
})
console.log('script end')複製代碼
// 答案
script start
a1 start
a2
promise2
script end
promise1
a1 end
promise2.then
promise3
setTimeout複製代碼

這跟上面差很少,只不過把promise2分開寫,實例化是同步的因此先運行log promise2


原文:

github.com/Advanced-Fr…

相關文章
相關標籤/搜索