詳解JavaScript執(zhí)行模型
JavaScript執(zhí)行模型
引言
JavaScript是一個單線程(Single-threaded)異步(Asynchronous)非阻塞(Non-blocking)并發(fā)(Concurrent)語言,這些語言效果通過一個調(diào)用棧(Call Stack)、一個事件循環(huán)(Event Loop)、一個回調(diào)隊列(Callback Queue)有些時候也叫任務隊列(Task Queue)與跟運行環(huán)境相關的API組成。
概念
調(diào)用棧 Call Stack
調(diào)用棧是一個LIFO后進先出數(shù)據(jù)結構的函數(shù)運行棧,它內(nèi)部的數(shù)據(jù)結構為函數(shù)幀。當在JavaScript中調(diào)用一個函數(shù)時,它將被壓入棧中,當這個函數(shù)內(nèi)部還有另一個函數(shù)被調(diào)用時,另一個函數(shù)將會被壓入棧頂,直到其內(nèi)部沒有更多調(diào)用,棧頂函數(shù)將會被以單線程方式執(zhí)行并出棧,直到最后一個函數(shù)幀出棧。JavaScript語言特性中的單線程就是指的調(diào)用棧的單線程運行。
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ù)也被壓入棧且沒有更多調(diào)用,JavaScript引擎開始執(zhí)行棧頂函數(shù)multiply(n, n)
返回結果并出棧,以此類推直到main()函數(shù)出棧。
調(diào)用棧有一個意外情況,當函數(shù)遞歸調(diào)用其自身時調(diào)用棧將溢出,執(zhí)行環(huán)境將報錯。
function foo() { foo(); } foo();
任務隊列 Task Queue
任務隊列是WebAPI的一部分,也就是說它本身并不是ECMAScript標準的一部分,而是運行環(huán)境自行實現(xiàn)的。任務隊列是所有回調(diào)函數(shù)排隊執(zhí)行的FIFO先進先出隊列,它的單位是任務(Task),每個任務都關聯(lián)著一個用于處理這個任務的回調(diào)函數(shù)。在事件循環(huán)(Event Loop)中會將任務隊列內(nèi)的函數(shù)壓入調(diào)用棧執(zhí)行并出隊列,直至為空。
任務隊列在瀏覽器的實現(xiàn)中被分為了宏任務隊列(macrotask queue)和微任務隊列(microtask queue),它們分別個自承載宏任務(macrotask)和微任務(microtask)的排隊,其中宏任務隊列與宏任務又被默認為常規(guī)的任務隊列與任務。
當調(diào)用棧內(nèi)所有調(diào)用都完成執(zhí)行后,事件輪詢會在每次處理宏任務隊列的一個宏任務后處理微任務隊列的全部微任務,也就是微任務基本會在宏任務處理之前被處理。微任務處理中間不會被UI或網(wǎng)絡事件處理被執(zhí)行,微任務執(zhí)行是連續(xù)的。
會被添加到宏任務的方法的回調(diào)有:
- script:script標簽中的代碼解析運行
- setTimeout
- setInterval
- setImmediate
- I/O
- UI rendering:UI渲染,每16.6ms放到隊列上一次,60fps,如果調(diào)用棧被占用則會被阻塞
會被添加到微任務的Web API方法有:
會被添加到微任務的Web API方法有:
- process.nextTick:Node提供的
- Promise
- Object.observe
- MutationObserver
微任務只會從我們編寫的代碼中產(chǎn)生,宏任務既可能從我們編寫的代碼中產(chǎn)生也可能從瀏覽器本身事件、渲染、IO產(chǎn)生。
事件循環(huán) Event Loop
事件循環(huán)是JavaScript的事件處理機制,它會一直輪詢消息隊列,當滿足調(diào)用棧為空且消息隊列不為空時,它將把消息隊列隊頭的消息壓入執(zhí)行棧。這樣的機制保證了函數(shù)不會被中斷,不會有線程切換帶來的數(shù)據(jù)不一致等情況
事件循環(huán)在調(diào)用棧為空時輪詢,順序為
1.找到任務隊列(宏任務隊列)的最早被添加的任務并將其添加到調(diào)用棧執(zhí)行
2.執(zhí)行所有微任務隊列內(nèi)的任務
- 當微任務隊列不為空時找到微任務隊列最早被添加的任務并將其添加到調(diào)用棧執(zhí)行
3.渲染所有變化
4.如果宏任務隊列為空等待宏任務出現(xiàn)
5.返回步驟1
JavaScript運行時 Runtime
瀏覽器的JavaScript代碼執(zhí)行也就是調(diào)用棧與堆(用于儲存變量對象等)由JavaScript引擎提供,用的比較多的是谷歌的V8引擎,Chrome、Edge瀏覽器、Nodejs均使用該引擎。
事件循環(huán)Event Loop、任務隊列Task Queue(回調(diào)隊列Callback Queue)、WebAPI或Node API由運行環(huán)境提供。
以上就是詳解JavaScript執(zhí)行模型的詳細內(nèi)容,更多關于JavaScript執(zhí)行模型的資料請關注腳本之家其它相關文章!
相關文章
JavaScript實現(xiàn)網(wǎng)頁上的浮動廣告的簡單方法
JavaScript實現(xiàn)網(wǎng)頁上的浮動廣告的簡單方法,需要的朋友可以參考一下2013-06-06asp.net+js 實現(xiàn)無刷新上傳解析csv文件的代碼
無刷新上傳解析csv文件的實現(xiàn)代碼,需要的朋友可以參考下。2010-05-05Javascript ParentNode和ChildNode接口原理解析
這篇文章主要介紹了Javascript ParentNode和ChildNode接口原理解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-03-03Javascript/Jquery——簡單定時器的多種實現(xiàn)方法
本文為大家詳細介紹下使用Javascript/Jquery實現(xiàn)簡單的定時器,方法有多種,大家可以根據(jù)自己的喜好自由選擇,希望對大家有所幫助2013-07-07