js消息任务队列
JS單線程、異步、同步概念
多次出現(xiàn)“事件循環(huán)”這個(gè)名詞,簡(jiǎn)單說(shuō)明了事件循環(huán)的步驟,以便理解nextTick的運(yùn)行時(shí)機(jī),這篇文章將更為詳細(xì)的分析下事件循環(huán)。在此之前需要了解JS單線程,及由此產(chǎn)生的同步執(zhí)行環(huán)境和異步執(zhí)行環(huán)境。
眾所周知,JS是單線程,雖然有webworker醬紫的多線程出現(xiàn),但也是在主線程的控制下。webworker僅僅能進(jìn)行計(jì)算任務(wù),不能操作DOM,所以本質(zhì)上還是單線程。
單線程即任務(wù)是串行的,后一個(gè)任務(wù)需要等待前一個(gè)任務(wù)的執(zhí)行,這就可能出現(xiàn)長(zhǎng)時(shí)間的等待。但由于類似ajax網(wǎng)絡(luò)請(qǐng)求、setTimeout時(shí)間延遲、DOM事件的用戶交互等,這些任務(wù)并不消耗 CPU,是一種空等,資源浪費(fèi),因此出現(xiàn)了異步。通過(guò)將任務(wù)交給相應(yīng)的異步模塊去處理,主線程的效率大大提升,可以并行的去處理其他的操作。當(dāng)異步處理完成,主線程空閑時(shí),主線程讀取相應(yīng)的callback,進(jìn)行后續(xù)的操作,最大程度的利用CPU。此時(shí)出現(xiàn)了同步執(zhí)行和異步執(zhí)行的概念,同步執(zhí)行是主線程按照順序,串行執(zhí)行任務(wù);異步執(zhí)行就是cpu跳過(guò)等待,先處理后續(xù)的任務(wù)(CPU與網(wǎng)絡(luò)模塊、timer等并行進(jìn)行任務(wù))。由此產(chǎn)生了任務(wù)隊(duì)列與事件循環(huán),來(lái)協(xié)調(diào)主線程與異步模塊之間的工作。
事件循環(huán)機(jī)制
?
step1:主線程讀取JS代碼,此時(shí)為同步環(huán)境,形成相應(yīng)的堆和執(zhí)行棧;
step2: ?主線程遇到異步任務(wù),指給對(duì)應(yīng)的異步進(jìn)程進(jìn)行處理(WEB API);
step3: ?異步進(jìn)程處理完畢(Ajax返回、DOM事件處罰、Timer到等),將相應(yīng)的異步任務(wù)推入任務(wù)隊(duì)列;
step4: 主線程執(zhí)行完畢,查詢?nèi)蝿?wù)隊(duì)列,如果存在任務(wù),則取出一個(gè)任務(wù)推入主線程處理(先進(jìn)先出);
step5: 重復(fù)執(zhí)行step2、3、4;稱為事件循環(huán)。
執(zhí)行的大意:
同步環(huán)境執(zhí)行(step1) -> 事件循環(huán)1(step4) -> 事件循環(huán)2(step4的重復(fù))…
其中的異步進(jìn)程有:
a、類似onclick等,由瀏覽器內(nèi)核的DOM binding模塊處理,事件觸發(fā)時(shí),回調(diào)函數(shù)添加到任務(wù)隊(duì)列中;
b、setTimeout等,由瀏覽器內(nèi)核的Timer模塊處理,時(shí)間到達(dá)時(shí),回調(diào)函數(shù)添加到任務(wù)隊(duì)列中;
c、Ajax,由瀏覽器內(nèi)核的Network模塊處理,網(wǎng)絡(luò)請(qǐng)求返回后,添加到任務(wù)隊(duì)列中。
任務(wù)隊(duì)列
如上示意圖,任務(wù)隊(duì)列存在多個(gè),同一任務(wù)隊(duì)列內(nèi),按隊(duì)列順序被主線程取走;不同任務(wù)隊(duì)列之間,存在著優(yōu)先級(jí),優(yōu)先級(jí)高的優(yōu)先獲取(如用戶I/O);
3.1、任務(wù)隊(duì)列的類型
任務(wù)隊(duì)列存在兩種類型,一種為microtask queue,另一種為macrotask queue。
圖中所列出的任務(wù)隊(duì)列均為macrotask queue,而ES6 的 promise[ECMAScript標(biāo)準(zhǔn)]產(chǎn)生的任務(wù)隊(duì)列為microtask queue。
? ? ? ? ? ??
3.2、兩者的區(qū)別
microtask queue:唯一,整個(gè)事件循環(huán)當(dāng)中,僅存在一個(gè);執(zhí)行為同步,同一個(gè)事件循環(huán)中的microtask會(huì)按隊(duì)列順序,串行執(zhí)行完畢;
macrotask queue:不唯一,存在一定的優(yōu)先級(jí)(用戶I/O部分優(yōu)先級(jí)更高);異步執(zhí)行,同一事件循環(huán)中,只執(zhí)行一個(gè)。
3.3、更完整的事件循環(huán)流程? ??
將microtask加入到JS運(yùn)行機(jī)制流程中,則:
step1、2、3同上,
step4:主線程查詢?nèi)蝿?wù)隊(duì)列,執(zhí)行microtask queue,將其按序執(zhí)行,全部執(zhí)行完畢;
step5:主線程查詢?nèi)蝿?wù)隊(duì)列,執(zhí)行macrotask queue,取隊(duì)首任務(wù)執(zhí)行,執(zhí)行完畢;
step6:重復(fù)step4、step5。
microtask queue中的所有callback處在同一個(gè)事件循環(huán)中,而macrotask queue中的callback有自己的事件循環(huán)。
簡(jiǎn)而言之:同步環(huán)境執(zhí)行 -> 事件循環(huán)1(microtask queue的All)->?事件循環(huán)2(macrotask queue中的一個(gè)) -> 事件循環(huán)1(microtask queue的All)->?事件循環(huán)2(macrotask queue中的一個(gè))...
利用microtask queue可以形成一個(gè)同步執(zhí)行的環(huán)境,但如果Microtask queue太長(zhǎng),將導(dǎo)致Macrotask任務(wù)長(zhǎng)時(shí)間執(zhí)行不了,最終導(dǎo)致用戶I/O無(wú)響應(yīng)等,所以使用需慎重。
示例、驗(yàn)證
???????????console.log('1, time = ' + new Date().toString())setTimeout(macroCallback, 0);new Promise(function(resolve, reject) {console.log('2, time = ' + new Date().toString())resolve();console.log('3, time = ' + new Date().toString())}).then(microCallback);function macroCallback() {console.log('4, time = ' + new Date().toString())} function microCallback() {console.log('5, time = ' + new Date().toString())}運(yùn)行結(jié)果如下:
1
2
3
5
4
運(yùn)行結(jié)果與預(yù)期一致,驗(yàn)證了在不同類型的任務(wù)隊(duì)列中,microtask queue中的callball將優(yōu)先執(zhí)行。
?? ?總結(jié):由此我們了解事件循環(huán)的機(jī)制,同時(shí)了解了任務(wù)隊(duì)列、JS主線程、異步操作之間的相互協(xié)作;同時(shí)認(rèn)識(shí)了兩種任務(wù)隊(duì)列:macrotask queue、microtask queue,它們由不同的標(biāo)準(zhǔn)制定,microtask queue對(duì)應(yīng)ECMAScript的promise屬性(ES6)和 DOM3的MutationObserver,文中說(shuō)明了兩者在事件循環(huán)中的運(yùn)行情況及區(qū)別;在今后的異步操作中,通過(guò)靈活運(yùn)用不同的任務(wù)隊(duì)列,提升用戶交互性能,給出更加的響應(yīng)和視覺(jué)體驗(yàn);同時(shí),通過(guò)JS的事件循環(huán)機(jī)制,可以更清楚JS代碼的執(zhí)行流,從而更好的控制代碼,更有效、更好的為業(yè)務(wù)服務(wù)。?
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
- 上一篇: vscode编辑器,自己喜欢的颜色
- 下一篇: 亿级数据库毫秒级查询?看完这一篇,海量数