最近閱讀《高性能JavaScript》時,第六章談到「經過定時器將JavaScript執行代碼的控制權先讓給瀏覽器用於更新UI狀態,而後再將控制權交回給JavaScript代碼,這樣就可使得頁面更爲流暢」,就聯想到了以前理解的事件循環。html
這篇文章就是爲了解釋爲何這麼作能夠提高頁面的流暢度。html5
總所周知,JavaScript語言的一大特色就是單線程,也就是說在一個時間段裏,JavaScript只能作一件事情(瀏覽器是多線程)。 多線程能夠實現應用的並行處理,從而以更高的CPU利用率提升整個應用程序的性能和吞吐量。web
可是JavaScript卻以單線程進行,爲何呢?數據庫
JavaScript是瀏覽器腳本語言,用於與用戶交互以及操做DOM。 考慮以下狀況,若是有兩個併發的操做,對同一個DOM節點分別進行刪除和修改樣式,此時瀏覽器就沒法決定到底採用哪一個線程的操做。相似數據庫,咱們能夠採用「鎖」來處理併發,可是這會平添複雜度。因此,JavaScript語言沒有支持多線程操做。 那又考慮這種狀況,既然JavaScript是單線程,在某一時刻內只能執行特定的一個任務,而且會阻塞其它任務執行。那麼若是用戶觸發了一個很是耗時的I/O操做,那麼按道理後續的全部操做都得等到I/O操做完成後方可進行。可是,事實上,後續的任務沒必要等待這個耗時的I/O操做完成,緣由就是JavaScript與生俱來的異步和回調。api
而這背後剛好就是本文的主題——————事件循環數組
事件循環包含了至少兩個任務隊列,宏任務隊列和微任務隊列。promise
宏任務包含建立文檔對象、解析HTML、執行主線JavaScript代碼、更改當前URL以及各類事件,例如頁面加載、輸入、網絡事件和定時器等等。宏任務運行完成後,瀏覽器繼續其餘的任務調度,如從新渲染頁面或者垃圾回收。瀏覽器
微任務包括promise、回調函數、DOM發生變化等。微任務更新應用程序的狀態,必須在瀏覽器任務繼續執行其餘任務(渲染UI視圖或者進行下一個宏任務)以前執行。網絡
在微任務隊列清空後,事件循環會檢查當前是否須要從新渲染UI,若是須要則渲染UI視圖。多線程
如今,用事件循環和簡單的例子來分析《高性能的JavaScript》中的那句話。 需求:給包含1000個數字的數組中的每一個元素取絕對值(假設對一個數字進行需求操做耗時1ms)。
狀況1(不使用定時器): 因爲JavaScript主線程代碼屬於宏任務的一種,因此一次事件循環須要處理1000個數字,因此1s事件循環才進行到UI更新階段,可是因爲耗時過長,UI狀態不會被更新,頁面出現卡頓甚至堵塞。
狀況2(使用定時器): 將一次處理1000個數字的任務分割爲20個每次處理50個數字的任務。因爲定時器是宏任務的一種,因此一次事件循環只處理50個數字,因爲此時微任務隊列爲空,因此50ms後事件循環進行到UI更新階段,而後根據狀況進行UI渲染,頁面未出現卡頓或者堵塞。
固然,若是隻是單純的處理數據,咱們能夠考慮使用Web Workers。