JS時間循環(huán)-宏任務(wù)與微任務(wù)
1、關(guān)于JavaScript
2、javascript事件循環(huán)
同步任務(wù) 異步任務(wù)

導(dǎo)圖要表達的內(nèi)容用文字來表述的話:
同步和異步任務(wù)分別進入不同的執(zhí)行"場所",同步的進入主線程,異步的進入Event Table并注冊函數(shù)。
當(dāng)指定的事情完成時,Event Table會將這個函數(shù)移入Event Queue。
主線程內(nèi)的任務(wù)執(zhí)行完畢為空,會去Event Queue讀取對應(yīng)的函數(shù),進入主線程執(zhí)行。
上述過程會不斷重復(fù),也就是常說的Event Loop(事件循環(huán))。
我們不禁要問了,那怎么知道主線程執(zhí)行棧為空啊?js引擎存在monitoring process進程,會持續(xù)不斷的檢查主線程執(zhí)行棧是否為空,一旦為空,就會去Event Queue那里檢查是否有等待被調(diào)用的函數(shù)。
3、Promise與process.nextTick(callback)
Promise的定義和功能本文不再贅述,不了解的讀者可以學(xué)習(xí)一下阮一峰老師的Promise。而process.nextTick(callback)類似node.js版的"setTimeout",在事件循環(huán)的下一次循環(huán)中調(diào)用 callback 回調(diào)函數(shù)。
我們進入正題,除了廣義的同步任務(wù)和異步任務(wù),我們對任務(wù)有更精細的定義:
macro-task(宏任務(wù)):包括整體代碼script,setTimeout,setInterval
micro-task(微任務(wù)):Promise,process.nextTick
不同類型的任務(wù)會進入對應(yīng)的Event Queue,比如setTimeout和setInterval會進入相同的Event Queue。
事件循環(huán)的順序,決定js代碼的執(zhí)行順序。進入整體代碼(宏任務(wù))后,開始第一次循環(huán)。接著執(zhí)行所有的微任務(wù)。然后再次從宏任務(wù)開始,找到其中一個任務(wù)隊列執(zhí)行完畢,再執(zhí)行所有的微任務(wù)。


事件循環(huán)的進程模型
選擇當(dāng)前要執(zhí)行的任務(wù)隊列,選擇任務(wù)隊列中最先進入的任務(wù),如果任務(wù)隊列為空即null,則執(zhí)行跳轉(zhuǎn)到微任務(wù)(MicroTask)的執(zhí)行步驟。
將事件循環(huán)中的任務(wù)設(shè)置為已選擇任務(wù)。
執(zhí)行任務(wù)。
將事件循環(huán)中當(dāng)前運行任務(wù)設(shè)置為null。
將已經(jīng)運行完成的任務(wù)從任務(wù)隊列中刪除。
microtasks步驟:進入microtask檢查點。
更新界面渲染。
返回第一步。

執(zhí)行棧在執(zhí)行完同步任務(wù)后,查看執(zhí)行棧是否為空,如果執(zhí)行棧為空,就會去檢查微任務(wù)(microTask)隊列是否為空,如果為空的話,就執(zhí)行Task(宏任務(wù)),否則就一次性執(zhí)行完所有微任務(wù)。
每次單個宏任務(wù)執(zhí)行完畢后,檢查微任務(wù)(microTask)隊列是否為空,如果不為空的話,會按照先入先出的規(guī)則全部執(zhí)行完微任務(wù)(microTask)后,設(shè)置微任務(wù)(microTask)隊列為null,然后再執(zhí)行宏任務(wù),如此循環(huán)。
例子:

?輸出結(jié)果:
1 7 6 8 2 4 3 5 9 11 10 12
4.process.nextTick和Promise都是Microtasks(微任務(wù)),為什么process.nextTick會先執(zhí)行?

rocess.nextTick 永遠大于 promise.then,原因其實很簡單。。。在Node中,_tickCallback在每一次執(zhí)行完TaskQueue中的一個任務(wù)后被調(diào)用,而這個_tickCallback中實質(zhì)上干了兩件事:
1、nextTickQueue中所有任務(wù)執(zhí)行掉(長度最大1e4,Node版本v6.9.1)
2、第一步執(zhí)行完后執(zhí)行_runMicrotasks函數(shù),執(zhí)行microtask(微任務(wù))中的部分(promise.then注冊的回調(diào))
所以很明顯 process.nextTick > promise.then
5、總結(jié)
(1)js的異步
我們從最開頭就說javascript是一門單線程語言,不管是什么新框架新語法糖實現(xiàn)的所謂異步,其實都是用同步的方法去模擬的,牢牢把握住單線程這點非常重要。
(2)事件循環(huán)Event Loop
事件循環(huán)是js實現(xiàn)異步的一種方法,也是js的執(zhí)行機制。
(3)javascript的執(zhí)行和運行
執(zhí)行和運行有很大的區(qū)別,javascript在不同的環(huán)境下,比如node,瀏覽器,Ringo等等,執(zhí)行方式是不同的。而運行大多指javascript解析引擎,是統(tǒng)一的。
(4)setImmediate
微任務(wù)和宏任務(wù)還有很多種類,比如setImmediate等等,執(zhí)行都是有共同點的,有興趣的同學(xué)可以自行了解。
(5)最后的最后
javascript是一門單線程語言
Event Loop是javascript的執(zhí)行機制
6、深入淺出分析process.nextTick()?
process.nextTick() 是 Node 的一個定時器,讓任務(wù)可以在指定的時間運行。其中 Node 一共提供了 4 個定時器,它們分別是 setTimeout()、setInterval()、setImmediate()、process.nextTick()。
process.nextTick()?這個名字有點誤導(dǎo),它是在本輪循環(huán)執(zhí)行的,而且是所有異步任務(wù)里面最快執(zhí)行的。
Node 執(zhí)行完所有同步任務(wù),接下來就會執(zhí)行process.nextTick的任務(wù)隊列。所以,下面這行代碼是第二個輸出結(jié)果。
process.nextTick(() => console.log(3));
基本上,如果你希望異步任務(wù)盡可能快地執(zhí)行,那就使用 process.nextTick。
根據(jù)語言規(guī)格,Promise 對象的回調(diào)函數(shù),會進入異步任務(wù)里面的”微任務(wù)”(microtask)隊列。
微任務(wù)隊列追加在 process.nextTick 隊列的后面,也屬于本輪循環(huán)。所以,下面的代碼總是先輸出 3,再輸出 4。
process.nextTick(() => console.log(3));Promise.resolve().then(() => console.log(4)); // 3 // 4注意,只有前一個隊列全部清空以后,才會執(zhí)行下一個隊列
process.nextTick(() => console.log(1));Promise.resolve().then(() => console.log(2));process.nextTick(() => console.log(3));Promise.resolve().then(() => console.log(4)); // 1 // 3 // 2 // 4上面代碼中,全部 process.nextTick 的回調(diào)函數(shù),執(zhí)行都會早于 Promise 的。
