JavaScript異步機制(二)之任務隊列和事件循環

在這個系列的第一篇調用棧以後,今天想說說任務隊列。 首先簡單說下瀏覽器的進程線程。以Chrome瀏覽器爲例,Chrome瀏覽器使用多個進程來隔離不一樣的網頁。所以在Chrome中打開一個網頁至關於起了一個進程。這一個進程中又包含多個線程,每一個線程分別處理不一樣的任務,好比渲染頁面,執行js等等。node

一個瀏覽器進程一般由如下五個線程組成:promise

  1. GUI渲染線程。
  2. JavaScript引擎線程。
  3. 事件觸發線程。
  4. 定時器線程。
  5. 異步HTTP請求線程。

GUI 渲染線程

GUI渲染線程負責渲染瀏覽器界面HTML元素,當界面須要重繪(Repaint)或因爲某種操做引起迴流(reflow)時,該線程就會執行。在Javascript引擎運行腳本期間,GUI渲染線程都是處於掛起狀態的,也就是說被」凍結」了。瀏覽器

JavaScript引擎線程

Javascript引擎,也能夠稱爲JS內核,主要負責處理Javascript腳本程序,例如V8引擎。Javascript引擎線程負責解析Javascript腳本,運行代碼。數據結構

事件觸發線程

當一個事件被觸發時該線程會把事件添加到待處理事件隊列的隊尾,等待JS引擎的處理。這些事件能夠是當前執行的代碼塊如定時任務、也可來自瀏覽器內核的其餘線程如鼠標點擊、AJAX異步請求等,但因爲JS的單線程關係全部這些事件都得排隊等待JS引擎處理。異步

定時器線程

瀏覽器定時計數器並非由JavaScript引擎計數的, 由於JavaScript引擎是單線程的,若是處於阻塞線程狀態就會影響記計時的準確, 所以經過單獨線程來計時並觸發定時是更爲合理的方案。函數

異步HTTP請求線程

在XMLHttpRequest在鏈接後經過瀏覽器新開一個線程請求,在檢測到狀態變動時,會觸發相應的事件,若是設置有回調函數,異步線程就產生狀態變動事件,將事件處理程序放到 JavaScript引擎的處理隊列中等待處理。線程

那麼什麼是任務隊列呢?server

任務隊列

你們都知道JS是單線程的,假如在JS引擎執行代碼的過程當中觸發了一些事件,那引擎會停下手中的事情來處理這些事件嗎?固然不可能。那何時才能執行這些事件處理程序呢?在js引擎空閒的時候。實際上,不管是事件觸發仍是定時器仍是HTTP請求,在各自的線程處理好對應的API以後,會把各自的事件處理程序放到一個叫作任務隊列的數據結構中。在JS引擎空閒的時候會按順序去執行每個事件處理程序。按怎樣的順序?先進隊列的先執行。這叫先進先出,和棧結構的後進先出正好相反。隊列

事件循環

當JS引擎執行完代碼以後,若是任務隊列中有待處理的事件處理程序,那麼JS引擎回去當即執行這些程序,那麼假如如今任務隊列是空的,可是過了一會有事件觸發了,JS引擎是如何知道的呢?這就要講到事件循環了。當JS引擎空閒的時候,他會不斷的是輪詢任務隊列,若是有任務的話就去執行。進程

微任務

微任務包括promise的回調,mutationObserver的回調,以及nodejs的process.nextTick的回調。

宏任務

script所有代碼。setTimeout,setInterval,setImmediate,I/O,UI Rendering

JS引擎初始執行代碼若是遇到微任務,會將微任務放到微任務隊列中,若是有宏任務,則將其放入宏任務隊列。執行完代碼後,會先去執行微任務隊列中的事件,而後就是不斷的事件循環。

咱們能夠把初始代碼當成一個宏任務,在執行完這個宏任務以後,JS引擎會去處理本輪宏任務結束時的微任務隊列,而後再去處理宏任務隊列。因此執行微任務的時機就是在宏任務與宏任務之間。

更新視圖

瀏覽器更新視圖的時機是由其本身決定的,並不必定是宏任務與宏任務之間必定有視圖的更新。能夠肯定的是,在JS引擎執行代碼時,是不會更新視圖的,由於GUI 渲染線程與JavaScript引擎線程互斥!瀏覽器會在適當的時機(宏任務與宏任務之間)更新視圖,而後再繼續執行代碼。

GUI 渲染線程 與 JavaScript引擎線程互斥!

因爲JavaScript是可操縱DOM的,若是在修改這些元素屬性同時渲染界面(即JavaScript線程和UI線程同時運行),那麼渲染線程先後得到的元素數據就可能不一致了。所以爲了防止渲染出現不可預期的結果,瀏覽器設置GUI渲染線程與JavaScript引擎爲互斥的關係,當JavaScript引擎執行時GUI線程會被掛起,GUI更新會被保存在一個隊列中等到引擎線程空閒時當即被執行。

相關文章
相關標籤/搜索