JavaScript的宏任務和微任務有哪些以及怎樣執(zhí)行的詳解
前言
Javascript 的執(zhí)行順序,眾所周知是按照順序自上而下執(zhí)行。
Javascript任務分為同步任務和異步任務,異步任務又分為宏任務和微任務,其中異步任務屬于耗時的任務。
一、宏任務和微任務是什么?
首先,js 是單線程語言,通俗理解就是,只有一個主線程,那么在任務多的情況下,就會出現(xiàn)死鎖等問題
單線程的局限性
單線程的局限性在于阻塞,如果一個任務耗時很長(如同步的無限循環(huán)或大量計算),會阻塞后續(xù)所有任務,導致頁面“卡死”。
那么如何解決單線程的缺陷?
JavaScript 通過 事件循環(huán)(Event Loop)實現(xiàn)非阻塞行為,意思是主線程會不斷檢查調(diào)用棧和任務隊列,當調(diào)用棧為空時,從任務隊列中取出回調(diào)任務執(zhí)行。
在 JavaScript 的事件循環(huán)(Event Loop)機制中,先執(zhí)行同步任務,再執(zhí)行異步任務,而異步任務分成了宏任務和微任務,宏任務和微任務決定了異步代碼的執(zhí)行順序。它們的區(qū)別在于執(zhí)行優(yōu)先級和觸發(fā)時機。
1.1宏任務
宏任務代表較大的、獨立的異步任務,由瀏覽器或 Node.js 安排在不同的任務隊列中執(zhí)行。
每次事件循環(huán)(Event Loop)會執(zhí)行一個宏任務,然后檢查并執(zhí)行所有微任務。
1.2.微任務
微任務是比宏任務更小的任務,通常用于處理高優(yōu)先級的異步操作。
每當一個宏任務執(zhí)行完畢,JS 引擎會立即清空整個微任務隊列,然后再執(zhí)行下一個宏任務。
二、宏任務、微任務是怎么執(zhí)行的?
首先,是先執(zhí)行同步代碼,遇到異步宏任務則將異步宏任務放入宏任務隊列中,遇到異步微任務則將異步微任務放入微任務隊列中,當所有同步代碼執(zhí)行完畢后,再將異步微任務從隊列中調(diào)入主線程執(zhí)行,微任務執(zhí)行完畢后再將異步宏任務從隊列中調(diào)入主線程執(zhí)行,一直循環(huán)直至所有任務執(zhí)行完畢。
注意:
這里容易錯誤的認為:就是微任務先于宏任務執(zhí)行!
其實不是,要明確:
1、那就是事件循環(huán)是從第一個宏任務開始!即:script 中的代碼,script 標簽(或模塊)的全局代碼是一個宏任務!
2、瀏覽器加載并執(zhí)行script 中的代碼時,這些同步代碼(如 console.log、變量聲明、函數(shù)調(diào)用等)會被視為第一個宏任務。
3、同步代碼執(zhí)行完后,才會處理微任務和其他宏任務
4、在 script 宏任務執(zhí)行期間,遇到的 Promise.then、MutationObserver 等微任務會被收集到微任務隊列。
5、同步代碼執(zhí)行完畢后,立即清空微任務隊列,然后才會處理下一個宏任務(如 setTimeout)。
宏任務會先進入主線程 但是執(zhí)行的時候會先執(zhí)行該宏任務當中的同步任務 然后執(zhí)行該宏任務中的微任務
當前宏任務執(zhí)行完畢后、下一個宏任務開始前,JavaScript 會清空所有微任務隊列。導致看上去是微任務比宏任務先執(zhí)行!
要理解事件循環(huán)的關鍵在于區(qū)分“加入隊列”和“執(zhí)行”這兩個步驟,以及明白微任務隊列的優(yōu)先級高于宏任務隊列。 雖然宏任務可能先進入隊列,但微任務會在當前宏任務執(zhí)行完畢后、下一個宏任務執(zhí)行前被優(yōu)先執(zhí)行。
三、舉個栗子
我舉個栗子,讓你明白執(zhí)行順序的問題
首先要明確 ,script中的整體代碼是最先執(zhí)行的任務!
宏任務:像你的“主要待辦事項”,
比如:
今天要寫作業(yè)(script主代碼)
1小時后取快遞(setTimeout)
明天去體檢(setInterval)
微任務:像“主要事項完成后立刻要做的小事”,
比如:
寫完作業(yè)后立刻收拾書包(Promise.then)
取完快遞后立刻拆包裝(MutationObserver)
console.log("開始寫作業(yè)"); // 宏任務1(大事) setTimeout(() => { console.log("1小時后:取快遞"); // 宏任務2(下一件大事) }, 0); Promise.resolve().then(() => { console.log("寫完作業(yè)后:收拾書包"); // 微任務(小事) });
那么執(zhí)行順序是:
先執(zhí)行第一個宏任務(script整體代碼),輸出 開始寫作業(yè) 和 作業(yè)寫完了。
立刻執(zhí)行這個宏任務產(chǎn)生的微任務(收拾書包)。
最后執(zhí)行下一個宏任務(取快遞)。
四、宏任務、微任務有哪些?
4.1宏任務
4.2 微任務
注意:
new Promise(…)是構造函數(shù),是同步代碼。
五、案例
<script> console.log('開始'); // 宏任務1 //宏任務A setTimeout(() => { console.log("宏任務A"); Promise.resolve().then(() => console.log("微任務A")); }, 0); //宏任務B setTimeout(() => { console.log('宏任務B'); }, 0); Promise.resolve().then(() => { console.log('微任務'); }); </script> //開始 微任務 宏任務A 微任務A 宏任務B
輸出過程:
代碼執(zhí)行的時候,先執(zhí)行全局代碼中的同步代碼 輸出:開始
然后執(zhí)行 全局代碼中的微任務 輸出 微任務
微任務執(zhí)行完畢后主線程會檢查調(diào)用棧把宏任務A調(diào)入進行執(zhí)行
但是會先執(zhí)行宏任務A中的同步任務會先輸出, 輸出 宏任務A
然后輸出微任務A,等到宏任務A全部執(zhí)行完畢后主線程會檢查調(diào)用棧調(diào)用棧會把 宏任務B調(diào)入進行執(zhí)行
但是宏任務B中沒有微任務 所以直接輸出 宏任務B這里我解釋一下 為什么 宏任務A比微任務A先輸出 ,因為宏任務A是該宏任務中的同步任務 所以先執(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
總結
到此這篇關于JavaScript的宏任務和微任務有哪些以及怎樣執(zhí)行的文章就介紹到這了,更多相關js宏任務和微任務內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
javascript 彈出窗口中是否顯示地址欄的實現(xiàn)代碼
程序中通過點擊一個“發(fā)貨提醒”鏈接彈出另一個窗口,使用的方法是用javascript 的openUrl()方法。2011-04-04Javascript 構造函數(shù),公有,私有特權和靜態(tài)成員定義方法
其中公有方法聲明的部分采用的兩種方式,在實際應用中一般采取一種方式就可以了,如果兩種方式都要采用的話,應注意順序,防止前面寫的方法被清空或覆蓋。2009-11-11