您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)深入淺析JS中的執(zhí)行模型,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。
JavaScript執(zhí)行模型
引言
JavaScript是一個(gè)單線程(Single-threaded)異步(Asynchronous)非阻塞(Non-blocking)并發(fā)(Concurrent)語(yǔ)言,這些語(yǔ)言效果通過(guò)一個(gè)調(diào)用棧(Call Stack)、一個(gè)事件循環(huán)(Event Loop)、一個(gè)回調(diào)隊(duì)列(Callback Queue)有些時(shí)候也叫任務(wù)隊(duì)列(Task Queue)與跟運(yùn)行環(huán)境相關(guān)的API組成。
概念
調(diào)用棧 Call Stack
調(diào)用棧是一個(gè)LIFO后進(jìn)先出數(shù)據(jù)結(jié)構(gòu)的函數(shù)運(yùn)行棧,它內(nèi)部的數(shù)據(jù)結(jié)構(gòu)為函數(shù)幀。當(dāng)在JavaScript中調(diào)用一個(gè)函數(shù)時(shí),它將被壓入棧中,當(dāng)這個(gè)函數(shù)內(nèi)部還有另一個(gè)函數(shù)被調(diào)用時(shí),另一個(gè)函數(shù)將會(huì)被壓入棧頂,直到其內(nèi)部沒(méi)有更多調(diào)用,棧頂函數(shù)將會(huì)被以單線程方式執(zhí)行并出棧,直到最后一個(gè)函數(shù)幀出棧。JavaScript語(yǔ)言特性中的單線程就是指的調(diào)用棧的單線程運(yùn)行。
function multiply(a, b) { return a * b; } function square(n) { return multiply(n, n) } function printSquare(n) { console.log(square(n)); } printSquare(4);
首先調(diào)用棧壓入main()
,掃描到printSquare()
函數(shù)調(diào)用調(diào)用棧壓入printSquare(4)
,printSquare
函數(shù)內(nèi)部調(diào)用square(n)
該函數(shù)被壓入棧,同理multiply(n, n)
函數(shù)也被壓入棧且沒(méi)有更多調(diào)用,JavaScript引擎開(kāi)始執(zhí)行棧頂函數(shù)multiply(n, n)
返回結(jié)果并出棧,以此類推直到main()函數(shù)出棧。
調(diào)用棧有一個(gè)意外情況,當(dāng)函數(shù)遞歸調(diào)用其自身時(shí)調(diào)用棧將溢出,執(zhí)行環(huán)境將報(bào)錯(cuò)。
function foo() { foo(); } foo();
任務(wù)隊(duì)列 Task Queue
任務(wù)隊(duì)列是WebAPI的一部分,也就是說(shuō)它本身并不是ECMAScript標(biāo)準(zhǔn)的一部分,而是運(yùn)行環(huán)境自行實(shí)現(xiàn)的。任務(wù)隊(duì)列是所有回調(diào)函數(shù)排隊(duì)執(zhí)行的FIFO先進(jìn)先出隊(duì)列,它的單位是任務(wù)(Task),每個(gè)任務(wù)都關(guān)聯(lián)著一個(gè)用于處理這個(gè)任務(wù)的回調(diào)函數(shù)。在事件循環(huán)(Event Loop)中會(huì)將任務(wù)隊(duì)列內(nèi)的函數(shù)壓入調(diào)用棧執(zhí)行并出隊(duì)列,直至為空。
任務(wù)隊(duì)列在瀏覽器的實(shí)現(xiàn)中被分為了宏任務(wù)隊(duì)列(macrotask queue)和微任務(wù)隊(duì)列(microtask queue),它們分別個(gè)自承載宏任務(wù)(macrotask)和微任務(wù)(microtask)的排隊(duì),其中宏任務(wù)隊(duì)列與宏任務(wù)又被默認(rèn)為常規(guī)的任務(wù)隊(duì)列與任務(wù)。
當(dāng)調(diào)用棧內(nèi)所有調(diào)用都完成執(zhí)行后,事件輪詢會(huì)在每次處理宏任務(wù)隊(duì)列的一個(gè)宏任務(wù)后處理微任務(wù)隊(duì)列的全部微任務(wù),也就是微任務(wù)基本會(huì)在宏任務(wù)處理之前被處理。微任務(wù)處理中間不會(huì)被UI或網(wǎng)絡(luò)事件處理被執(zhí)行,微任務(wù)執(zhí)行是連續(xù)的。
會(huì)被添加到宏任務(wù)的方法的回調(diào)有:
會(huì)被添加到微任務(wù)的Web API方法有:
會(huì)被添加到微任務(wù)的Web API方法有:
微任務(wù)只會(huì)從我們編寫的代碼中產(chǎn)生,宏任務(wù)既可能從我們編寫的代碼中產(chǎn)生也可能從瀏覽器本身事件、渲染、IO產(chǎn)生。
事件循環(huán) Event Loop
事件循環(huán)是JavaScript的事件處理機(jī)制,它會(huì)一直輪詢消息隊(duì)列,當(dāng)滿足調(diào)用棧為空且消息隊(duì)列不為空時(shí),它將把消息隊(duì)列隊(duì)頭的消息壓入執(zhí)行棧。這樣的機(jī)制保證了函數(shù)不會(huì)被中斷,不會(huì)有線程切換帶來(lái)的數(shù)據(jù)不一致等情況
事件循環(huán)在調(diào)用棧為空時(shí)輪詢,順序?yàn)?/p>
1.找到任務(wù)隊(duì)列(宏任務(wù)隊(duì)列)的最早被添加的任務(wù)并將其添加到調(diào)用棧執(zhí)行
2.執(zhí)行所有微任務(wù)隊(duì)列內(nèi)的任務(wù)
3.渲染所有變化
4.如果宏任務(wù)隊(duì)列為空等待宏任務(wù)出現(xiàn)
5.返回步驟1
JavaScript運(yùn)行時(shí) Runtime
瀏覽器的JavaScript代碼執(zhí)行也就是調(diào)用棧與堆(用于儲(chǔ)存變量對(duì)象等)由JavaScript引擎提供,用的比較多的是谷歌的V8引擎,Chrome、Edge瀏覽器、Nodejs均使用該引擎。
事件循環(huán)Event Loop、任務(wù)隊(duì)列Task Queue(回調(diào)隊(duì)列Callback Queue)、WebAPI或Node API由運(yùn)行環(huán)境提供。
關(guān)于深入淺析JS中的執(zhí)行模型就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。