摘要:引擎線程,也稱為內核,負責處理腳本程序,例如引擎。異步請求線程,也就是發出請求后,接收響應檢測狀態變更等都是這個線程管理的。為了解決這個問題,提出標準,允許腳本創建多個線程,但是子線程完全受主線程控制,且不得操作。
本文主要參閱了以下兩篇文章,對JS的Event Loop運行機制基礎知識進行了整理。
從瀏覽器多進程到JS單線程,JS運行機制最全面的一次梳理
JavaScript 運行機制詳解:再談Event Loop
大家都知道JavaScript是單線程的,這就引申出一個問題,進程與線程是什么,他們的區別是什么?
先給出進程和線程的定義:
進程是cpu資源分配的最小單位(是能擁有資源和獨立運行的最小單位)
線程是cpu調度的最小單位(線程是建立在進程的基礎上的一次程序運行單位,一個進程中可以有多個線程)
用工廠和工人的例子來形象闡述:
- 進程是一個工廠,工廠有它的獨立資源 -> 系統分配的內存(獨立的一塊內存) - 工廠之間相互獨立 -> 進程之間相互獨立 - 線程是工廠中的工人,多個工人協作完成任務 -> 多個線程在進程中協作完成任務 - 工廠內有一個或多個工人 -> 一個進程由一個或多個線程組成 - 工人之間共享工廠的資源 -> 同一進程下的各個線程之間共享進程的內存空間(包括代碼段、數據集、堆等)
補充:
我們所說的單線程和多線程,是指一個進程內是單一線程還是多線程。
進程間的通信方式包括: 管道pipe、 命名管道FIFO、消息隊列MessageQueue、共享存儲SharedMemory、信號量Semaphore、套接字Socket、信號。
瀏覽器是多進程的關于瀏覽器進程問題可以簡單基礎三點:
瀏覽器是多進程的。
瀏覽器之所以能夠運行,是因為系統給它的進程分配了資源(cpu、內存)。
簡單點理解,每打開一個Tab頁,就相當于創建了一個獨立的瀏覽器進程。
平時 coding 接觸到最多的一個瀏覽器進程是瀏覽器渲染進程(瀏覽器內核),它管理著頁面渲染。腳本執行,事件處理等。要同時處理這么多事情,渲染進程顯然是多線程的,它主要包括以下5個常駐線程:
GUI渲染線程,負責渲染瀏覽器界面,解析HTML,CSS,構建DOM樹和RenderObject樹,布局和繪制等。
JS引擎線程,也稱為JS內核,負責處理Javascript腳本程序,(例如V8引擎)。
事件觸發線程,用來控制事件循環(可以理解為,JS引擎線程自己都忙不過來,需要瀏覽器另開線程協助)。
定時觸發器線程,瀏覽器定時計數器并不是由JavaScript引擎計數的,(因為JavaScript引擎是單線程的, 如果處于阻塞線程狀態就會影響記計時的準確),JS中常用的setInterval和setTimeout就歸這個線程管理。
異步http請求線程,也就是ajax發出http請求后,接收響應、檢測狀態變更等都是這個線程管理的。
我們常說的JavaScript是單線程的,其實就是說的JS引擎是單線程的,它僅僅是瀏覽器渲染進程種的一個線程。為什么呢?因為JavaScript的主要作用是與用戶互動,以及操作DOM,如果JavaScript有兩個線程,一個線程對一個DOM節點執行 A 操作,另一個線程這個DOM節點執行 B 操作,那么就會起沖突,所以JavaScript在前端的應用就注定了它是單線程的。
然而JavaScript的單線程特性就注定我們不用它去完成密集的 cpu 運算,因為密集 cpu 運算耗時過長,阻塞頁面渲染。為了解決這個問題,HTML5提出 Web Worker 標準,允許JavaScript腳本創建多個線程,但是子線程完全受主線程控制,且不得操作DOM。
Event LoopJavaScript 是單線程的帶來的問題是:所有任務都必須同步執行,問題就出現了,很多 I/O 過程是非常耗時的(如http 請求數據),如果要等到 I/O 過程結束再執行后續任務,就會出現頁面的卡頓、cpu 的閑置。于是異步的任務就出現了,異步任務是指掛起處于等待中的任務,繼續執行同步任務,等到結果返回再去繼續執行被掛起的任務。于是,JavaScript 的任務可以分為同步任務和異步任務。下面就引出 Event Loop 機制:
所有同步任務都在主線程上執行,形成一個執行棧
主線程之外,事件觸發線程管理著一個任務隊列,只要異步任務有了運行結果,就在任務隊列之中放置一個事件。
一旦執行棧中的所有同步任務執行完畢(此時JS引擎空閑),系統就會讀取任務隊列,將可運行的異步任務添加到執行棧中,開始執行。
如上圖所示,執行棧中的代碼會調用一個異步的API,它們會在任務隊列中添加各種事件(或者說回調函數),另外用戶的操作如click、mousedown等都會在任務隊列中添加事件。只要執行棧中的代碼執行完畢,主線程就會去讀取任務隊列,將可執行的回調函數放到執行棧中執行。
總結一下:
執行棧執行完畢 -> 主線程讀取任務隊列,并執行回調函數 -> 執行棧執行完畢 -> 主線程讀取任務隊列,并執行回調函數 ...
這個過程一直循環下去,所以就叫事件循環(Event Loop)。
setTimeout 和 setInterval前面提到了瀏覽器的定時觸發器線程,它的主要作用就是計時,setTimeout 和 setInterval 就由它來控制,原理就是到達設置時間后,往任務隊列中添加這兩個函數中指定的回調函數。
setTimeout() 方法用于在指定的毫秒數后調用函數或計算表達式。但是需要注意的是,實際是計時結束后定時觸發器線程才會將回調函數放到任務隊列中去,此時任務隊列中這個回調之前可能已經有一些事件待處理,并且一定要執行棧的任務執行完后才會開始執行任務隊列中的任務,所以 setTimeout() 中回調開始執行的時間是:執行棧執行時間 + 任務隊列前方回調執行時間 + 延遲時間
setInterval() 方法可按照指定的時間間隔來周期性調用函數或計算表達式。它的問題在于:每次都精確的隔一段時間將一個回調放到任務隊列中,并沒有考慮到內部回調函數執行所需時間,這就會導致兩種問題:
回調函數執行需要時間,兩個函數執行的時間間隔會小于設定值;
如果回調函數執行時間大于設定間隔,就會出現上一個加入任務隊列中的回調還沒執行完,下一個回調就被加入任務隊列了,就會出現累計效應,即后面的回調會連續執行。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/96459.html
摘要:規范中定義了瀏覽器何時進行渲染更新,了解它有助于性能優化。結合一些資料,對上邊規范給出一些理解有誤請指正每個線程都有自己的。列為,列為,列為。我們都知道是單線程,渲染計算和腳本運行共用同一線程網絡請求會有其他線程,導致腳本運行會阻塞渲染。 本文轉自blog 轉載請注明出處 異步的思考 event loops隱藏得比較深,很多人對它很陌生。但提起異步,相信每個人都知道。異步背后的靠山就是...
摘要:但是導致了很明顯的性能問題。上述兩個例子其實是在這個中找到的,第一個使用的版本是,這個版本的實現是采用了,而后因為的里的有,于是尤雨溪更改了實現,換成了,也就是后一個所使用的。后來尤雨溪了解到是將回調放入的隊列。 結論 對于event loop 可以抽象成一段簡單的代碼表示 for (macroTask of macroTaskQueue) { // 1. Handle cur...
摘要:但是導致了很明顯的性能問題。上述兩個例子其實是在這個中找到的,第一個使用的版本是,這個版本的實現是采用了,而后因為的里的有,于是尤雨溪更改了實現,換成了,也就是后一個所使用的。后來尤雨溪了解到是將回調放入的隊列。 結論 對于event loop 可以抽象成一段簡單的代碼表示 for (macroTask of macroTaskQueue) { // 1. Handle cur...
摘要:事件循環了解了在引擎中是如何工作了之后,來看下如何使用異步回調函數來避免代碼。從回調函數被放入后秒鐘,把移到中。由于事件循環持續地監測調用棧是否已空,此時它一注意到調用??樟耍驼{用并創建一個新的調用棧。 聽多了JavaScript單線程,異步,V8,便會很想去知道JavaScript是如何利用單線程來實現所謂的異步的。我參考了一些文章,了解到一個很重要的詞匯:事件循環(Event L...
摘要:主線程在任務隊列中讀取事件,這個過程是循環不斷地,所以這種運行機制叫做事件循環是在執行棧同步代碼結束之后,下一次任務隊列執行之前。 單線程 javascript為什么是單線程語言,原因在于如果是多線程,當一個線程對DOM節點做添加內容操作的時候,另一個線程要刪除這個DOM節點,這個時候,瀏覽器應該怎么選擇,這就造成了混亂,為了解決這類問題,在一開始的時候,javascript就采用單線...
閱讀 2416·2021-11-25 09:43
閱讀 1195·2021-09-07 10:16
閱讀 2603·2021-08-20 09:38
閱讀 2936·2019-08-30 15:55
閱讀 1449·2019-08-30 13:21
閱讀 883·2019-08-29 15:37
閱讀 1435·2019-08-27 10:56
閱讀 2093·2019-08-26 13:45