本文主要參閱瞭如下兩篇文章,對JS的Event Loop運行機制基礎知識進行了整理。
從瀏覽器多進程到JS單線程,JS運行機制最全面的一次梳理
JavaScript 運行機制詳解:再談Event Loophtml
你們都知道JavaScript是單線程的,這就引伸出一個問題,進程與線程是什麼,他們的區別是什麼?
先給出進程和線程的定義:前端
用工廠和工人的例子來形象闡述:ajax
- 進程是一個工廠,工廠有它的獨立資源 -> 系統分配的內存(獨立的一塊內存) - 工廠之間相互獨立 -> 進程之間相互獨立 - 線程是工廠中的工人,多個工人協做完成任務 -> 多個線程在進程中協做完成任務 - 工廠內有一個或多個工人 -> 一個進程由一個或多個線程組成 - 工人之間共享工廠的資源 -> 同一進程下的各個線程之間共享進程的內存空間(包括代碼段、數據集、堆等)
補充:segmentfault
關於瀏覽器進程問題能夠簡單基礎三點:瀏覽器
平時 coding 接觸到最多的一個瀏覽器進程是瀏覽器渲染進程(瀏覽器內核),它管理着頁面渲染。腳本執行,事件處理等。要同時處理這麼多事情,渲染進程顯然是多線程的,它主要包括如下5個常駐線程:多線程
setInterval
和setTimeout
就歸這個線程管理。ajax
發出http請求後,接收響應、檢測狀態變動等都是這個線程管理的。咱們常說的JavaScript是單線程的,其實就是說的JS引擎是單線程的,它僅僅是瀏覽器渲染進程種的一個線程。爲何呢?由於JavaScript的主要做用是與用戶互動,以及操做DOM
,若是JavaScript有兩個線程,一個線程對一個DOM
節點執行 A 操做,另外一個線程這個DOM
節點執行 B 操做,那麼就會起衝突,因此JavaScript在前端的應用就註定了它是單線程的。異步
然而JavaScript的單線程特性就註定咱們不用它去完成密集的 cpu 運算,由於密集 cpu 運算耗時過長,阻塞頁面渲染。爲了解決這個問題,HTML5提出 Web Worker 標準,容許JavaScript腳本建立多個線程,可是子線程徹底受主線程控制,且不得操做DOM
。函數
JavaScript 是單線程的帶來的問題是:全部任務都必須同步執行,問題就出現了,不少 I/O 過程是很是耗時的(如http 請求數據),若是要等到 I/O 過程結束再執行後續任務,就會出現頁面的卡頓、cpu 的閒置。因而異步的任務就出現了,異步任務是指掛起處於等待中的任務,繼續執行同步任務,等到結果返回再去繼續執行被掛起的任務。因而,JavaScript 的任務能夠分爲同步任務和異步任務。下面就引出 Event Loop 機制:oop
如上圖所示,執行棧中的代碼會調用一個異步的API,它們會在任務隊列中添加各類事件(或者說回調函數),另外用戶的操做如click
、mousedown
等都會在任務隊列中添加事件。只要執行棧中的代碼執行完畢,主線程就會去讀取任務隊列,將可執行的回調函數放到執行棧中執行。佈局
總結一下:
執行棧執行完畢 -> 主線程讀取任務隊列,並執行回調函數 -> 執行棧執行完畢 -> 主線程讀取任務隊列,並執行回調函數 ...
這個過程一直循環下去,因此就叫事件循環(Event Loop)。
前面提到了瀏覽器的定時觸發器線程,它的主要做用就是計時,setTimeout
和 setInterval
就由它來控制,原理就是到達設置時間後,往任務隊列中添加這兩個函數中指定的回調函數。
setTimeout()
方法用於在指定的毫秒數後調用函數或計算表達式。可是須要注意的是,實際是計時結束後定時觸發器線程纔會將回調函數放到任務隊列中去,此時任務隊列中這個回調以前可能已經有一些事件待處理,而且必定要執行棧的任務執行完後纔會開始執行任務隊列中的任務,因此 setTimeout()
中回調開始執行的時間是:執行棧執行時間 + 任務隊列前方回調執行時間 + 延遲時間
setInterval()
方法可按照指定的時間間隔來週期性調用函數或計算表達式。它的問題在於:每次都精確的隔一段時間將一個回調放到任務隊列中,並無考慮到內部回調函數執行所需時間,這就會致使兩種問題: