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

4.2 微任務(wù)

注意:
new Promise(…)是構(gòu)造函數(shù),是同步代碼。
五、案例
<script>
console.log('開始'); // 宏任務(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>
//開始 微任務(wù) 宏任務(wù)A 微任務(wù)A 宏任務(wù)B
輸出過程:
代碼執(zhí)行的時候,先執(zhí)行全局代碼中的同步代碼 輸出:開始
然后執(zhí)行 全局代碼中的微任務(wù) 輸出 微任務(wù)
微任務(wù)執(zhí)行完畢后主線程會檢查調(diào)用棧把宏任務(wù)A調(diào)入進行執(zhí)行
但是會先執(zhí)行宏任務(wù)A中的同步任務(wù)會先輸出, 輸出 宏任務(wù)A
然后輸出微任務(wù)A,等到宏任務(wù)A全部執(zhí)行完畢后主線程會檢查調(diào)用棧調(diào)用棧會把 宏任務(wù)B調(diào)入進行執(zhí)行
但是宏任務(wù)B中沒有微任務(wù) 所以直接輸出 宏任務(wù)B這里我解釋一下 為什么 宏任務(wù)A比微任務(wù)A先輸出 ,因為宏任務(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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 淺談JavaScript宏任務(wù)和微任務(wù)執(zhí)行順序
- 淺談js中的宏任務(wù)和微任務(wù)
- JavaScript中的宏任務(wù)和微任務(wù)執(zhí)行順序
- JavaScript宏任務(wù)和微任務(wù)區(qū)別介紹
- JS事件循環(huán)機制event loop宏任務(wù)微任務(wù)原理解析
- JavaScript?微任務(wù)和宏任務(wù)講解
- 詳解JS事件循環(huán)及宏任務(wù)微任務(wù)的原理
- JavaScript事件循環(huán)及宏任務(wù)微任務(wù)原理解析
- 淺談javascript事件環(huán)微任務(wù)和宏任務(wù)隊列原理
- JavaScript中的宏任務(wù)和微任務(wù)詳情
相關(guān)文章
javascript 彈出窗口中是否顯示地址欄的實現(xiàn)代碼
程序中通過點擊一個“發(fā)貨提醒”鏈接彈出另一個窗口,使用的方法是用javascript 的openUrl()方法。2011-04-04
Javascript 構(gòu)造函數(shù),公有,私有特權(quán)和靜態(tài)成員定義方法
其中公有方法聲明的部分采用的兩種方式,在實際應(yīng)用中一般采取一種方式就可以了,如果兩種方式都要采用的話,應(yīng)注意順序,防止前面寫的方法被清空或覆蓋。2009-11-11

