JavaScript是在JavaScript 引擎中執(zhí)行的,當(dāng)拿到一段 JavaScript 代碼時苗分,瀏覽器或者 Node 環(huán)境首先要做的就是;傳遞給 JavaScript 引擎谆奥,并且要求它去執(zhí)行。
然而嚷掠,執(zhí)行 JavaScript 并非一錘子買賣,宿主環(huán)境當(dāng)遇到一些事件時荞驴,會繼續(xù)把一段代碼傳遞給 JavaScript 引擎去執(zhí)行不皆,此外,我們可能還會提供 API 給 JavaScript 引擎熊楼,比如 setTimeout 這樣的 API霹娄,它會允許 JavaScript 在特定的時機(jī)執(zhí)行。所以鲫骗,我們首先應(yīng)該形成一個感性的認(rèn)知:一個 JavaScript 引擎會常駐于內(nèi)存中犬耻,它等待著我們(宿主)把 JavaScript 代碼或者函數(shù)傳遞給它執(zhí)行。在 ES3 和更早的版本中执泰,JavaScript 本身還沒有異步執(zhí)行代碼的能力枕磁,這也就意味著,宿主環(huán)境傳遞給 JavaScript 引擎一段代碼术吝,引擎就把代碼直接順次執(zhí)行了计济,這個任務(wù)也就是宿主發(fā)起的任務(wù)。但是排苍,在 ES5 之后沦寂,JavaScript 引入了 Promise,這樣淘衙,不需要瀏覽器的安排传藏,JavaScript 引擎本身也可以發(fā)起任務(wù)了。
我們把宿主發(fā)起的任務(wù)稱為宏觀任務(wù)幔翰,把 JavaScript 引擎發(fā)起的任務(wù)稱為微觀任務(wù)
宏觀和微觀任務(wù)JavaScript 引擎等待宿主環(huán)境分配宏觀任務(wù)漩氨,在操作系統(tǒng)中西壮,通常等待的行為都是一個事件循環(huán)遗增,所以在 Node 術(shù)語中,也會把這個部分稱為事件循環(huán)款青。不過做修,術(shù)語本身并非我們需要重點討論的內(nèi)容,我們在這里把重點放在事件循環(huán)的原理上抡草。在底層的 C/C++ 代碼中饰及,這個事件循環(huán)是一個跑在獨立線程中的循環(huán),我們用偽代碼來表示康震,大概是這樣的:while(TRUE) { r = wait(); execute(r);}我們可以看到燎含,整個循環(huán)做的事情基本上就是反復(fù)“等待 - 執(zhí)行”。當(dāng)然腿短,實際的代碼中并沒有這么簡單屏箍,還有要判斷循環(huán)是否結(jié)束绘梦、宏觀任務(wù)隊列等邏輯,這里為了方便你理解赴魁,我就把這些都省略掉了卸奉。這里每次的執(zhí)行過程,其實都是一個宏觀任務(wù)颖御。我們可以大概理解:宏觀任務(wù)的隊列就相當(dāng)于事件循環(huán)榄棵。在宏觀任務(wù)中,JavaScript 的 Promise 還會產(chǎn)生異步代碼潘拱,JavaScript 必須保證這些異步代碼在一個宏觀任務(wù)中完成疹鳄,因此,每個宏觀任務(wù)中又包含了一個微觀任務(wù)隊列泽铛。
js執(zhí)行過程
是通過的事件循環(huán)(event loop),理解了event loop機(jī)制,就理解了JS的執(zhí)行機(jī)制尚辑。
同步和異步任務(wù)分別進(jìn)入不同的執(zhí)行"場所",同步的進(jìn)入主線程盔腔,異步的進(jìn)入Event Table并注冊函數(shù)杠茬。
當(dāng)指定的事情完成時,Event Table會將這個函數(shù)移入Event Queue弛随。
主線程內(nèi)的任務(wù)執(zhí)行完畢為空瓢喉,會去Event Queue讀取對應(yīng)的函數(shù),進(jìn)入主線程執(zhí)行舀透。
上述過程會不斷重復(fù)栓票,也就是常說的Event Loop(事件循環(huán))。
我們不禁要問了愕够,那怎么知道主線程執(zhí)行棧為空白咛啊?js引擎存在monitoring process進(jìn)程惑芭,會持續(xù)不斷的檢查主線程執(zhí)行棧是否為空坠狡,一旦為空,就會去Event Queue那里檢查是否有等待被調(diào)用的函數(shù)遂跟。