什么是事件循環(huán)?想要了解什么是事件循環(huán)就要從js的工作原理開始說起: JS主要的特點(diǎn)就是單線程,所謂單線程就是進(jìn)程中只有一個(gè)線程在運(yùn)行。 為什么JS是單線程的而不是多線程的呢? JS的主要用途就是與用戶交互,操作DOM,假設(shè)JS同時(shí)有兩個(gè)線程,一個(gè)線程中在某個(gè)DOM節(jié)點(diǎn)上添加或者修改內(nèi)容,而另一個(gè)線程在這個(gè)DOM節(jié)點(diǎn)上執(zhí)行刪除該節(jié)點(diǎn)操作,這樣就會(huì)產(chǎn)生沖突。 單線程就意味著所有任務(wù)都需要排隊(duì),前一任務(wù)結(jié)束,才會(huì)執(zhí)行后一個(gè)任務(wù),當(dāng)是如果當(dāng)遇到前一個(gè)任務(wù)耗時(shí)很長的情況,后一個(gè)任務(wù)就不得不一直等著。因此,就有了同步任務(wù)、異步任務(wù)。 同步任務(wù)和異步任務(wù)在js中是如何執(zhí)行的呢? js的代碼運(yùn)行會(huì)形成一個(gè)主線程和一個(gè)任務(wù)隊(duì)列。主線程會(huì)自上而下依次執(zhí)行我們的js代碼,形成一個(gè)執(zhí)行棧。 同步任務(wù)就會(huì)被放到這個(gè)主線程中依次執(zhí)行。而異步任務(wù)被放入到任務(wù)隊(duì)列中執(zhí)行,執(zhí)行完就會(huì)在任務(wù)隊(duì)列中打一個(gè)標(biāo)記,形成一個(gè)對(duì)應(yīng)的事件。當(dāng)主線程中的任務(wù)全部運(yùn)行完畢,js會(huì)去提取并執(zhí)行任務(wù)隊(duì)列中的事件。這個(gè)過程是循環(huán)進(jìn)行的,這就是EventLoop。 JS引擎執(zhí)行異步代碼不用等待,是因?yàn)橛惺录?duì)列和事件循環(huán)。 事件循環(huán)是指主線程重復(fù)從事件隊(duì)列中取消息、執(zhí)行的過程。指整個(gè)執(zhí)行流程。 事件隊(duì)列是一個(gè)存儲(chǔ)著待執(zhí)行任務(wù)的序列,其中的任務(wù)嚴(yán)格按照時(shí)間先后順序執(zhí)行,排在隊(duì)頭的任務(wù)會(huì)率先執(zhí)行,而排在隊(duì)尾的任務(wù)會(huì)最后執(zhí)行。(即先進(jìn)先出) 事件隊(duì)列:
事件循環(huán)運(yùn)行機(jī)制 (1)執(zhí)行一個(gè)宏任務(wù)(棧中沒有就從事件隊(duì)列中獲?。?/p> (2)執(zhí)行過程中如果遇到微任務(wù),就將它添加到微任務(wù)的任務(wù)隊(duì)列中; (3)宏任務(wù)執(zhí)行完畢后,立即執(zhí)行當(dāng)前微任務(wù)隊(duì)列的所有微任務(wù); (4)當(dāng)前微任務(wù)執(zhí)行完畢,開始檢查渲染,然后GUI線程接管渲染; (5)渲染完畢后,JS線程繼續(xù)接管,開始下一個(gè)宏任務(wù)。
事例: async function async1() { console.log("async1 start"); //(2) await async2(); console.log("async1 end"); //(6) } async function async2() { console.log( 'async2'); //(3) } console.log("script start"); //(1) setTimeout(function () { console.log("settimeout"); //(8) },0); async1(); new Promise(function (resolve) { console.log("promise1"); //(4) resolve(); }).then(function () { console.log("promise2"); //(7) }); console.log('script end');//(5)
按照事件循環(huán)機(jī)制分析以上代碼運(yùn)行流程: 1. 首先,事件循環(huán)從宏任務(wù)(macrotask)隊(duì)列開始,首先讀取script(整體代碼)任務(wù);當(dāng)遇到任務(wù)源(task source)時(shí),則會(huì)先分發(fā)任務(wù)到對(duì)應(yīng)的任務(wù)隊(duì)列中去。 2. 然后我們看到首先定義了兩個(gè)async函數(shù),此時(shí)沒有調(diào)用,接著往下看,然后遇到了 `console` 語句,直接輸出 `script start`。輸出之后,script 任務(wù)繼續(xù)往下執(zhí)行,遇到 `setTimeout`,其作為一個(gè)宏任務(wù)源,則會(huì)先將其任務(wù)分發(fā)到對(duì)應(yīng)的任務(wù)隊(duì)列中。 3. script 任務(wù)繼續(xù)往下執(zhí)行,執(zhí)行了async1()函數(shù),async函數(shù)中在await之前的代碼是立即執(zhí)行的,所以會(huì)立即輸出`async1 start`。 4. script任務(wù)繼續(xù)往下執(zhí)行,遇到Promise實(shí)例。由于Promise中的函數(shù)是立即執(zhí)行的,而后續(xù)的 `.then` 則會(huì)被分發(fā)到 microtask 的 `Promise` 隊(duì)列中去。所以會(huì)先輸出 `promise1`,然后執(zhí)行 `resolve`,將 `promise2` 分配到對(duì)應(yīng)隊(duì)列。 5. script任務(wù)繼續(xù)往下執(zhí)行,輸出了 `script end`,至此,全局任務(wù)就執(zhí)行完畢了。 6. 第二輪循環(huán)依舊從宏任務(wù)隊(duì)列開始。此時(shí)宏任務(wù)中只有一個(gè) `setTimeout`,取出直接輸出即可,至此整個(gè)流程結(jié)束。 |
|