深入分析JavaScript 事件循環(huán)(Event Loop)
事件循環(huán)(Event Loop),是每個JS開發(fā)者都會接觸到的概念,但是剛接觸時可能會存在各種疑惑。
眾所周知,JS是單線程的,即同一時間只能運行一個任務(wù)。一般情況下這不會引發(fā)問題,但是如果我們有一個耗時較多的任務(wù),我們必須等該任務(wù)執(zhí)行完畢才能進入下一個任務(wù),然而等待的這段時間常常讓我們無法忍受,因為我們這段時間什么都不能做,包括頁面也是鎖死狀態(tài)。
好在,時代在進步,瀏覽器向我們提供了JS引擎不具備的特性:Web API。Web API包括DOM API、定時器、HTTP請求等特性,可以幫助我們實現(xiàn)異步、非阻塞的行為。我們可以通過異步執(zhí)行任務(wù)的方法來解決單線程的弊端,事件循環(huán)為此而生。
提問QAQ:為什么JavaScript是單線程的?
多個線程表示您可以同時獨立執(zhí)行程序的多個部分。確定一種語言是單線程還是多線程的最簡單方法是看它擁有有多少個調(diào)用堆棧。JS 只有一個,所以它是單線程語言。
將JS設(shè)計為單線程是由其用途運行環(huán)境等因素決定的,作為瀏覽器腳本語言,JS的主要用途是與用戶互動,以及操作DOM。這決定了它只能是單線程,否則會帶來很復(fù)雜的同步問題。同時,單線程執(zhí)行效率高。
1. Event Loop舊印象
大家熟悉的關(guān)于事件循環(huán)的機制說法大概是:主進程執(zhí)行完了之后,每次從任務(wù)隊列里取一個任務(wù)執(zhí)行。如圖所示,所有的任務(wù)分為同步任務(wù)和異步任務(wù),同步任務(wù)直接進入任務(wù)隊列-->主程序執(zhí)行;異步任務(wù)則會掛起,等待其有返回值時進入任務(wù)隊列從而被主程序執(zhí)行。異步任務(wù)會通過任務(wù)隊列的機制(先進先出的機制)來進行協(xié)調(diào)。具體如圖所示:

同步和異步任務(wù)分別進入不同的執(zhí)行環(huán)境,同步的進入主線程,即主執(zhí)行棧,異步的進入任務(wù)隊列。主線程內(nèi)的任務(wù)執(zhí)行完畢為空,會去任務(wù)隊列讀取對應(yīng)的任務(wù),推入主線程執(zhí)行。 上述過程的不斷重復(fù)就是我們所熟悉的Event Loop (事件循環(huán))。但是promise出現(xiàn)之后,這個說法就不太準(zhǔn)確了。
2. Event Loop 后印象
2.1 理論
這里首先用一張圖展示JavaScript的事件循環(huán):

直接看這張圖,可能黑人問號已經(jīng)出現(xiàn)在同學(xué)的腦海。。。
這里將task分為兩大類,分別是macroTask(宏任務(wù))和microTask(微任務(wù)).一次事件循環(huán):先運行macroTask隊列中的一個,然后運行microTask隊列中的所有任務(wù)。接著開始下一次循環(huán)(只是針對macroTask和microTask,一次完整的事件循環(huán)會比這個復(fù)雜的多)。
那什么是macroTask?什么是microTask呢?
JavaScript引擎把我們的所有任務(wù)分門別類,一部分歸為macroTask,另外一部分歸為microTack,下面是類別劃分:
macroTask:
- setTimeout
- setInterval
- setImmediate
- requestAnimationFrame
- I/O
- UI rendering
microTask:
- process.nextTick
- Promise
- Object.observe
- MutationObserver
我們所熟悉的定時器就屬于macroTask,僅僅了解macroTask的機制還是不夠的。為直觀感受兩種隊列的區(qū)別,下面上代碼進行實踐感知。
2.2 實踐
以setTimeout、process.nextTick、promise為例直觀感受下兩種任務(wù)隊列的運行方式。
console.log('main1');
process.nextTick(function() {
console.log('process.nextTick1');
});
setTimeout(function() {
console.log('setTimeout');
process.nextTick(function() {
console.log('process.nextTick2');
});
}, 0);
new Promise(function(resolve, reject) {
console.log('promise');
resolve();
}).then(function() {
console.log('promise then');
});
console.log('main2');
別著急看答案,先以上面的理論自己想想,運行結(jié)果會是啥?
最終結(jié)果是這樣的:
main1
promise
main2
process.nextTick1
promise then// 第二次事件循環(huán)
setTimeout
process.nextTick2
process.nextTick 和 promise then在 setTimeout 前面輸出,已經(jīng)證明了macroTask和microTask的執(zhí)行順序。但是有一點必須要指出的是。上面的圖容易給人一個錯覺,就是主進程的代碼執(zhí)行之后,會先調(diào)用macroTask,再調(diào)用microTask,這樣在第一個循環(huán)里一定是macroTask在前,microTask在后。
但是最終的實踐證明:在第一個循環(huán)里,process.nextTick1和promise then這兩個microTask是在setTimeout這個macroTask里之前輸出的,這是因為Promises/A+規(guī)范規(guī)定主進程的代碼也屬于macroTask。
主進程這個macroTask(也就是main1、promise和main2)執(zhí)行完了,自然會去執(zhí)行process.nextTick1和promise then這兩個microTask。這是第一個循環(huán)。之后的setTimeout和process.nextTick2屬于第二個循環(huán)
別看上面那段代碼好像特別繞,把原理弄清楚了,都一樣 ~
requestAnimationFrame、Object.observe(已廢棄) 和 MutationObserver這三個任務(wù)的運行機制大家可以從上面看到,不同的只是具體用法不同。重點說下UI rendering。在HTML規(guī)范:event-loop-processing-model里敘述了一次事件循環(huán)的處理過程,在處理了macroTask和microTask之后,會進行一次Update the rendering,其中細(xì)節(jié)比較多,總的來說會進行一次UI的重新渲染。
3. 小結(jié)
總而言之,記住一次事件循環(huán):先運行macroTask隊列中的一個,然后運行microTask隊列中的所有任務(wù)。接著開始下一次循環(huán)。
參考文獻:
以上就是深入分析JavaScript 事件循環(huán)(Event Loop)的詳細(xì)內(nèi)容,更多關(guān)于JavaScript 事件循環(huán)(Event Loop)的資料請關(guān)注腳本之家其它相關(guān)文章!
- JavaScript事件循環(huán)及宏任務(wù)微任務(wù)原理解析
- JS事件循環(huán)機制event loop宏任務(wù)微任務(wù)原理解析
- 詳解node.js 事件循環(huán)
- javascript事件循環(huán)event loop的簡單模型解釋與應(yīng)用分析
- Nodejs監(jiān)控事件循環(huán)異常示例詳解
- 前端js中的事件循環(huán)eventloop機制詳解
- 詳解JS瀏覽器事件循環(huán)機制
- javascript中的event loop事件循環(huán)詳解
- 深入淺析Node.js 事件循環(huán)、定時器和process.nextTick()
- 實例分析js事件循環(huán)機制
- 實例分析JS與Node.js中的事件循環(huán)
- 在實例中重學(xué)JavaScript事件循環(huán)
相關(guān)文章
javascript 學(xué)習(xí)筆記(六)瀏覽器類型及版本信息檢測代碼
由于各瀏覽器對css以及js支持的差異性,我們在做前端開發(fā)時,經(jīng)常需要先檢測瀏覽器的類型及版本,然后會對各自的差異性來寫代碼!2011-04-04
深入分析JavaScript 事件循環(huán)(Event Loop)
這篇文章主要介紹了JavaScript 事件循環(huán)(Event Loop)的的相關(guān)資料,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-06-06
javascrpt綁定事件之匿名函數(shù)無法解除綁定問題
經(jīng)常聽到有人說,匿名函數(shù)綁定事件不好控制啊,無法解除綁定啊,本文將介紹詳細(xì)的解決方法,需要的朋友可以參考下2012-12-12

