JavaScript的宏任務(wù)和微任務(wù)有哪些以及怎樣執(zhí)行的詳解
前言
Javascript 的執(zhí)行順序,眾所周知是按照順序自上而下執(zhí)行。
Javascript任務(wù)分為同步任務(wù)和異步任務(wù),異步任務(wù)又分為宏任務(wù)和微任務(wù),其中異步任務(wù)屬于耗時(shí)的任務(wù)。
一、宏任務(wù)和微任務(wù)是什么?
首先,js 是單線(xiàn)程語(yǔ)言,通俗理解就是,只有一個(gè)主線(xiàn)程,那么在任務(wù)多的情況下,就會(huì)出現(xiàn)死鎖等問(wèn)題
單線(xiàn)程的局限性
單線(xiàn)程的局限性在于阻塞,如果一個(gè)任務(wù)耗時(shí)很長(zhǎng)(如同步的無(wú)限循環(huán)或大量計(jì)算),會(huì)阻塞后續(xù)所有任務(wù),導(dǎo)致頁(yè)面“卡死”。
那么如何解決單線(xiàn)程的缺陷?
JavaScript 通過(guò) 事件循環(huán)(Event Loop)實(shí)現(xiàn)非阻塞行為,意思是主線(xiàn)程會(huì)不斷檢查調(diào)用棧和任務(wù)隊(duì)列,當(dāng)調(diào)用棧為空時(shí),從任務(wù)隊(duì)列中取出回調(diào)任務(wù)執(zhí)行。
在 JavaScript 的事件循環(huán)(Event Loop)機(jī)制中,先執(zhí)行同步任務(wù),再執(zhí)行異步任務(wù),而異步任務(wù)分成了宏任務(wù)和微任務(wù),宏任務(wù)和微任務(wù)決定了異步代碼的執(zhí)行順序。它們的區(qū)別在于執(zhí)行優(yōu)先級(jí)和觸發(fā)時(shí)機(jī)。
1.1宏任務(wù)
宏任務(wù)代表較大的、獨(dú)立的異步任務(wù),由瀏覽器或 Node.js 安排在不同的任務(wù)隊(duì)列中執(zhí)行。
每次事件循環(huán)(Event Loop)會(huì)執(zhí)行一個(gè)宏任務(wù),然后檢查并執(zhí)行所有微任務(wù)。
1.2.微任務(wù)
微任務(wù)是比宏任務(wù)更小的任務(wù),通常用于處理高優(yōu)先級(jí)的異步操作。
每當(dāng)一個(gè)宏任務(wù)執(zhí)行完畢,JS 引擎會(huì)立即清空整個(gè)微任務(wù)隊(duì)列,然后再執(zhí)行下一個(gè)宏任務(wù)。
二、宏任務(wù)、微任務(wù)是怎么執(zhí)行的?
首先,是先執(zhí)行同步代碼,遇到異步宏任務(wù)則將異步宏任務(wù)放入宏任務(wù)隊(duì)列中,遇到異步微任務(wù)則將異步微任務(wù)放入微任務(wù)隊(duì)列中,當(dāng)所有同步代碼執(zhí)行完畢后,再將異步微任務(wù)從隊(duì)列中調(diào)入主線(xiàn)程執(zhí)行,微任務(wù)執(zhí)行完畢后再將異步宏任務(wù)從隊(duì)列中調(diào)入主線(xiàn)程執(zhí)行,一直循環(huán)直至所有任務(wù)執(zhí)行完畢。
注意:
這里容易錯(cuò)誤的認(rèn)為:就是微任務(wù)先于宏任務(wù)執(zhí)行!
其實(shí)不是,要明確:
1、那就是事件循環(huán)是從第一個(gè)宏任務(wù)開(kāi)始!即:script 中的代碼,script 標(biāo)簽(或模塊)的全局代碼是一個(gè)宏任務(wù)!
2、瀏覽器加載并執(zhí)行script 中的代碼時(shí),這些同步代碼(如 console.log、變量聲明、函數(shù)調(diào)用等)會(huì)被視為第一個(gè)宏任務(wù)。
3、同步代碼執(zhí)行完后,才會(huì)處理微任務(wù)和其他宏任務(wù)
4、在 script 宏任務(wù)執(zhí)行期間,遇到的 Promise.then、MutationObserver 等微任務(wù)會(huì)被收集到微任務(wù)隊(duì)列。
5、同步代碼執(zhí)行完畢后,立即清空微任務(wù)隊(duì)列,然后才會(huì)處理下一個(gè)宏任務(wù)(如 setTimeout)。
宏任務(wù)會(huì)先進(jìn)入主線(xiàn)程 但是執(zhí)行的時(shí)候會(huì)先執(zhí)行該宏任務(wù)當(dāng)中的同步任務(wù) 然后執(zhí)行該宏任務(wù)中的微任務(wù)
當(dāng)前宏任務(wù)執(zhí)行完畢后、下一個(gè)宏任務(wù)開(kāi)始前,JavaScript 會(huì)清空所有微任務(wù)隊(duì)列。導(dǎo)致看上去是微任務(wù)比宏任務(wù)先執(zhí)行!
要理解事件循環(huán)的關(guān)鍵在于區(qū)分“加入隊(duì)列”和“執(zhí)行”這兩個(gè)步驟,以及明白微任務(wù)隊(duì)列的優(yōu)先級(jí)高于宏任務(wù)隊(duì)列。 雖然宏任務(wù)可能先進(jìn)入隊(duì)列,但微任務(wù)會(huì)在當(dāng)前宏任務(wù)執(zhí)行完畢后、下一個(gè)宏任務(wù)執(zhí)行前被優(yōu)先執(zhí)行。
三、舉個(gè)栗子
我舉個(gè)栗子,讓你明白執(zhí)行順序的問(wèn)題
首先要明確 ,script中的整體代碼是最先執(zhí)行的任務(wù)!
宏任務(wù):像你的“主要待辦事項(xiàng)”,
比如:
今天要寫(xiě)作業(yè)(script主代碼)
1小時(shí)后取快遞(setTimeout)
明天去體檢(setInterval)
微任務(wù):像“主要事項(xiàng)完成后立刻要做的小事”,
比如:
寫(xiě)完作業(yè)后立刻收拾書(shū)包(Promise.then)
取完快遞后立刻拆包裝(MutationObserver)
console.log("開(kāi)始寫(xiě)作業(yè)"); // 宏任務(wù)1(大事)
setTimeout(() => {
console.log("1小時(shí)后:取快遞"); // 宏任務(wù)2(下一件大事)
}, 0);
Promise.resolve().then(() => {
console.log("寫(xiě)完作業(yè)后:收拾書(shū)包"); // 微任務(wù)(小事)
});
那么執(zhí)行順序是:
先執(zhí)行第一個(gè)宏任務(wù)(script整體代碼),輸出 開(kāi)始寫(xiě)作業(yè) 和 作業(yè)寫(xiě)完了。
立刻執(zhí)行這個(gè)宏任務(wù)產(chǎn)生的微任務(wù)(收拾書(shū)包)。
最后執(zhí)行下一個(gè)宏任務(wù)(取快遞)。
四、宏任務(wù)、微任務(wù)有哪些?
4.1宏任務(wù)

4.2 微任務(wù)

注意:
new Promise(…)是構(gòu)造函數(shù),是同步代碼。
五、案例
<script>
console.log('開(kāi)始'); // 宏任務(wù)1
//宏任務(wù)A
setTimeout(() => {
console.log("宏任務(wù)A");
Promise.resolve().then(() => console.log("微任務(wù)A"));
}, 0);
//宏任務(wù)B
setTimeout(() => {
console.log('宏任務(wù)B');
}, 0);
Promise.resolve().then(() => {
console.log('微任務(wù)');
});
</script>
//開(kāi)始 微任務(wù) 宏任務(wù)A 微任務(wù)A 宏任務(wù)B
輸出過(guò)程:
代碼執(zhí)行的時(shí)候,先執(zhí)行全局代碼中的同步代碼 輸出:開(kāi)始
然后執(zhí)行 全局代碼中的微任務(wù) 輸出 微任務(wù)
微任務(wù)執(zhí)行完畢后主線(xiàn)程會(huì)檢查調(diào)用棧把宏任務(wù)A調(diào)入進(jìn)行執(zhí)行
但是會(huì)先執(zhí)行宏任務(wù)A中的同步任務(wù)會(huì)先輸出, 輸出 宏任務(wù)A
然后輸出微任務(wù)A,等到宏任務(wù)A全部執(zhí)行完畢后主線(xiàn)程會(huì)檢查調(diào)用棧調(diào)用棧會(huì)把 宏任務(wù)B調(diào)入進(jìn)行執(zhí)行
但是宏任務(wù)B中沒(méi)有微任務(wù) 所以直接輸出 宏任務(wù)B這里我解釋一下 為什么 宏任務(wù)A比微任務(wù)A先輸出 ,因?yàn)楹耆蝿?wù)A是該宏任務(wù)中的同步任務(wù) 所以先執(zhí)行輸出
setTimeout(function(){
console.log('1');
});
new Promise(function(resolve){
console.log('2');
resolve();
}).then(function(){
console.log('3');
}).then(function(){
console.log('4')
});
console.log('5');
// 2 5 3 4 1
console.log("Script start");
setTimeout(() => {
console.log("setTimeout");
}, 0);
Promise.resolve()
.then(() => {
console.log("Promise 1");
})
.then(() => {
console.log("Promise 2");
});
console.log("Script end"); 、
//Script start
//Script end
//Promise 1
//Promise 2
//setTimeout
setTimeout(()=>{
new Promise(resolve =>{
resolve();
}).then(()=>{
console.log('test');
});
console.log(4);
});
new Promise(resolve => {
resolve();
console.log(1)
}).then( () => {
console.log(3);
Promise.resolve().then(() => {
console.log('before timeout');
}).then(() => {
Promise.resolve().then(() => {
console.log('also before timeout')
})
})
})
console.log(2);
// 1 2 3 before timeout also before timeout 4 test
總結(jié)
到此這篇關(guān)于JavaScript的宏任務(wù)和微任務(wù)有哪些以及怎樣執(zhí)行的文章就介紹到這了,更多相關(guān)js宏任務(wù)和微任務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 淺談JavaScript宏任務(wù)和微任務(wù)執(zhí)行順序
- 淺談js中的宏任務(wù)和微任務(wù)
- JavaScript中的宏任務(wù)和微任務(wù)執(zhí)行順序
- JavaScript宏任務(wù)和微任務(wù)區(qū)別介紹
- JS事件循環(huán)機(jī)制event loop宏任務(wù)微任務(wù)原理解析
- JavaScript?微任務(wù)和宏任務(wù)講解
- 詳解JS事件循環(huán)及宏任務(wù)微任務(wù)的原理
- JavaScript事件循環(huán)及宏任務(wù)微任務(wù)原理解析
- 淺談javascript事件環(huán)微任務(wù)和宏任務(wù)隊(duì)列原理
- JavaScript中的宏任務(wù)和微任務(wù)詳情
相關(guān)文章
javascript如何操作HTML下拉列表標(biāo)簽
下拉列表在網(wǎng)站前端開(kāi)發(fā)中經(jīng)常遇到,如何操作html下拉列表標(biāo)簽,本篇文章給大家詳解javascript如何操作html下拉列表標(biāo)簽,需要的朋友可以來(lái)參考下2015-08-08
javaScript中push函數(shù)用法實(shí)例分析
這篇文章主要介紹了javaScript中push函數(shù)用法,較為詳細(xì)的分析了javascript中push函數(shù)的功能、定義及使用技巧,需要的朋友可以參考下2015-06-06
javascript 彈出窗口中是否顯示地址欄的實(shí)現(xiàn)代碼
程序中通過(guò)點(diǎn)擊一個(gè)“發(fā)貨提醒”鏈接彈出另一個(gè)窗口,使用的方法是用javascript 的openUrl()方法。2011-04-04
Javascript 構(gòu)造函數(shù),公有,私有特權(quán)和靜態(tài)成員定義方法
其中公有方法聲明的部分采用的兩種方式,在實(shí)際應(yīng)用中一般采取一種方式就可以了,如果兩種方式都要采用的話(huà),應(yīng)注意順序,防止前面寫(xiě)的方法被清空或覆蓋。2009-11-11
w3c編程挑戰(zhàn)_初級(jí)腳本算法實(shí)戰(zhàn)篇
下面小編就為大家?guī)?lái)一篇w3c編程挑戰(zhàn)_初級(jí)腳本算法實(shí)戰(zhàn)篇。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06
bootstrap基礎(chǔ)知識(shí)學(xué)習(xí)筆記
這篇文章主要針對(duì)bootstrap基礎(chǔ)知識(shí)為大家整理了詳細(xì)的學(xué)習(xí)筆記,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11
javascript屬性訪(fǎng)問(wèn)表達(dá)式用法分析
這篇文章主要介紹了javascript屬性訪(fǎng)問(wèn)表達(dá)式用法,實(shí)例分析了javascript屬性訪(fǎng)問(wèn)表達(dá)式的功能與使用方法,需要的朋友可以參考下2015-04-04

