宏任務(wù)(macrotask )和微任務(wù)(microtask )
宏任務(wù)和微任務(wù)都是我們?cè)陂_發(fā)工作中經(jīng)常用到的漠另。
宏任務(wù)(macrotask )和微任務(wù)(microtask )
macrotask 和 microtask 表示異步任務(wù)的兩種分類。
在掛起任務(wù)時(shí),JS 引擎
會(huì)將所有任務(wù)按照類別分到這兩個(gè)隊(duì)列中颅筋,首先在 macrotask
的隊(duì)列(這個(gè)隊(duì)列也被叫做 task queue
)中取出第一個(gè)任務(wù)擒悬,執(zhí)行完畢后取出 microtask
隊(duì)列中的所有任務(wù)順序執(zhí)行秀鞭;之后再取 macrotask
任務(wù)趋观,周而復(fù)始,直至兩個(gè)隊(duì)列的任務(wù)都取完锋边。
“先查看是否有事件可執(zhí)行”,如果有這優(yōu)先級(jí)限制编曼,那應(yīng)該可以這樣理解:
其實(shí)并不止一個(gè)消息隊(duì)列豆巨,有異步隊(duì)列
和事件隊(duì)列
,而事件隊(duì)列總是優(yōu)先于異步隊(duì)列被空閑下來的JS線程取用
宏任務(wù)一般是:包括整體代碼script
掐场,setTimeout
往扔,setInterval
、I/O
熊户、UI render
萍膛。
微任務(wù)主要是:Promise
、Object.observe
嚷堡、MutationObserver
蝗罗。
-
宏任務(wù)和微任務(wù)之間的關(guān)系
區(qū)別體現(xiàn)demo
- 1.Promise在前,setTimeout在后
new Promise((resolve) => {
console.log('外層宏事件2');
resolve()
}).then(() => {
console.log('微事件1');
}).then(()=>{
console.log('微事件2')
})
console.log('外層宏事件1');
setTimeout(() => {
//執(zhí)行后 回調(diào)一個(gè)宏事件
console.log('內(nèi)層宏事件3')
}, 0)
結(jié)果:
外層宏事件2
外層宏事件1
微事件1
微事件2
內(nèi)層宏事件3
- 2.setTimeout在前蝌戒,Promise在后
setTimeout(() => {
//執(zhí)行后 回調(diào)一個(gè)宏事件
console.log('內(nèi)層宏事件3')
}, 0)
console.log('外層宏事件1');
new Promise((resolve) => {
console.log('外層宏事件2');
resolve()
}).then(() => {
console.log('微事件1');
}).then(()=>{
console.log('微事件2')
})
結(jié)果:
外層宏事件1
外層宏事件2
微事件1
微事件2
內(nèi)層宏事件3
NodeJS的宏任務(wù)和微任務(wù)跟瀏覽器環(huán)境又有什么區(qū)別
瀏覽器的Event loop是在HTML5中定義的規(guī)范串塑,而node中則由libuv庫(kù)實(shí)現(xiàn)。libuv庫(kù)流程大體分為6個(gè)階段:timers北苟,I/O callbacks桩匪,idle、prepare友鼻,poll傻昙,check,close callbacks彩扔,和瀏覽器的microtask妆档,macrotask那一套有區(qū)別。
兩者執(zhí)行的規(guī)則不同借杰,所以不相同过吻。
// js event loop (指主線程從“任務(wù)隊(duì)列”中循環(huán)讀取任務(wù))
//
// event queue 可能同時(shí)不止一個(gè)
//
// 宏任務(wù)(macrotask)event queue (macro-task(宏任務(wù)):script(主程序代碼),,setTimeout,setInterval, I/O, setImmediate(node環(huán)境),UI rendering)
// ^
// | 異步
// 主線程->
// | | 異步
// | V
// | 微任務(wù)(microtask) event queue (micro-task(微任務(wù)):Promise纤虽,process.nextTick(node環(huán)境)乳绕,Object.observe, MutationObserver)
// |
// V
// 如果是瀏覽器環(huán)境 click等事件隊(duì)列總是優(yōu)先于異步隊(duì)列被空閑下來的JS線程取用
// 執(zhí)行順序 script(主程序代碼)—>process.nextTick—>Promises…——>setTimeout——>setInterval——>setImmediate——> I/O——>UI rendering
// nextTick 優(yōu)先級(jí)比 Promise 等 microTask 高,setTimeout和setInterval優(yōu)先級(jí)比setImmediate高
瀏覽器環(huán)境
瀏覽器環(huán)境下的 異步任務(wù) 分為 宏任務(wù)(macroTask) 和 微任務(wù)(microTask):
宏任務(wù)(macroTask):script
中代碼逼纸、setTimeout
洋措、setInterval
、I/O
杰刽、UI render
菠发;
微任務(wù)(microTask): Promise
、Object.observe
贺嫂、MutationObserver
滓鸠。
當(dāng)滿足執(zhí)行條件時(shí),宏任務(wù)
(macroTask) 和 微任務(wù)
(microTask) 會(huì)各自被放入對(duì)應(yīng)的隊(duì)列:宏隊(duì)列
(Macrotask Queue) 和 微隊(duì)列
(Microtask Queue) 中等待執(zhí)行第喳。
Node 環(huán)境
在 Node 環(huán)境中 任務(wù)類型 相對(duì)就比瀏覽器環(huán)境下要復(fù)雜一些:
microTask
:微任務(wù)糜俗;
nextTick
:process.nextTick;
timers
:執(zhí)行滿足條件的 setTimeout 曲饱、setInterval 回調(diào)悠抹;
I/O callbacks
:是否有已完成的 I/O 操作的回調(diào)函數(shù),來自上一輪的 poll 殘留扩淀;
poll
:等待還沒完成的 I/O 事件楔敌,會(huì)因 timers 和超時(shí)時(shí)間等結(jié)束等待;
check
:執(zhí)行 setImmediate 的回調(diào)驻谆;
close callbacks
:關(guān)閉所有的 closing handles 卵凑,一些 onclose 事件;
idle/prepare 等等:可忽略旺韭。
因此氛谜,也就產(chǎn)生了執(zhí)行事件循環(huán)相應(yīng)的任務(wù)隊(duì)列 Timers Queue、I/O Queue区端、Check Queue 和 Close Queue值漫。
2.執(zhí)行過程
瀏覽器環(huán)境
先執(zhí)行<script>
中的同步任務(wù),然后所有微任務(wù)织盼,一個(gè)宏任務(wù)杨何,所有微任務(wù),一個(gè)宏任務(wù)......
執(zhí)行完主執(zhí)行線程中的任務(wù)沥邻;
取出 Microtask Queue 中任務(wù)執(zhí)行直到清空危虱;
取出 Macrotask Queue 中一個(gè)任務(wù)執(zhí)行;
重復(fù) 2 和 3 唐全。
需要 注意 的是:
在瀏覽器頁(yè)面中可以認(rèn)為初始執(zhí)行線程中沒有代碼埃跷,每一個(gè)<script>
中的代碼是一個(gè)獨(dú)立的 task 蕊玷,即會(huì)執(zhí)行完前面的<script>
中創(chuàng)建的 microTask
再執(zhí)行后面的<script>
中的同步代碼;
如果 microTask
一直被添加弥雹,則會(huì)繼續(xù)執(zhí)行 microTask 垃帅,“卡死” macroTask;
部分版本瀏覽器有執(zhí)行順序與上述不符的情況剪勿,可能是不符合標(biāo)準(zhǔn)或 js 與 html 部分標(biāo)準(zhǔn)沖突贸诚;
Promise 的then
和catch
才是 microTask ,本身的內(nèi)部代碼不是厕吉;
個(gè)別瀏覽器獨(dú)有API未列出酱固。
Node 環(huán)境
循環(huán)之前
在進(jìn)入第一次循環(huán)之前,會(huì)先進(jìn)行如下操作:
同步任務(wù)头朱;
發(fā)出異步請(qǐng)求运悲;
規(guī)劃定時(shí)器生效的時(shí)間;
執(zhí)行process.nextTick()
髓窜。
開始循環(huán)
循環(huán)中進(jìn)行的操作:
清空當(dāng)前循環(huán)內(nèi)的 Timers Queue
扇苞,清空 NextTick Queue
,清空 Microtask Queue
寄纵;
清空當(dāng)前循環(huán)內(nèi)的 I/O Queue
,清空NextTick Queue
脖苏,清空 Microtask Queue
程拭;
清空當(dāng)前循環(huán)內(nèi)的 Check Queue
,清空 NextTick Queue
棍潘,清空 Microtask Queue
恃鞋;
清空當(dāng)前循環(huán)內(nèi)的 Close Queue
,清空 NextTick Queue
亦歉,清空Microtask Queue
恤浪;
進(jìn)入下輪循環(huán)。
可以看出肴楷,nextTick
優(yōu)先級(jí)比 Promise
等 microTask
高水由,setTimeout
和setInterval
優(yōu)先級(jí)比setImmediate
高。
注意
在整個(gè)過程中赛蔫,需要 注意 的是:
如果在 timers 階段執(zhí)行時(shí)創(chuàng)建了setImmediate 則會(huì)在此輪循環(huán)的 check 階段執(zhí)行砂客,如果在 timers 階段創(chuàng)建了setTimeout,由于 timers 已取出完畢呵恢,則會(huì)進(jìn)入下輪循環(huán)鞠值,check 階段創(chuàng)建 timers 任務(wù)同理;
setTimeout優(yōu)先級(jí)比setImmediate高渗钉,但是由于setTimeout(fn,0)的真正延遲不可能完全為 0 秒彤恶,可能出現(xiàn)先創(chuàng)建的setTimeout(fn,0)而比setImmediate的回調(diào)后執(zhí)行的情況。