從耗時的角度,瀏覽器請求、加載、渲染一個頁面,時間花在下面五件事情上:
1.DNS 查詢
2.TCP 鏈接
3.HTTP 請求即響應
4.服務器響應
5.客戶端渲染javascript
這裏重點討論第五個部分,即瀏覽器對內容的渲染,這一部分(渲染樹構建,佈局和繪製),又能夠分爲下面的五個部分。java
1.處理 HTML 標記並構建 DOM 樹。
2.處理 CSS 標記並構建 CSSOM 樹。
3.將 DOM 與 CSSOM 合併成一個渲染樹。
4.根據渲染樹來佈局,以計算每一個節點的幾何信息。
5.將各個節點繪製到屏幕上。node
這些並非本文正文,只是說在完成以上過程以後,整個頁面就已經出來了,這個時候瀏覽器是否就已經處於空閒狀態了呢(不考慮動畫、交互等處理)?接下來就是本文的重點了。git
瞭解重點以前咱們先了解一點簡單的基礎知識,堆、棧、隊列;github
對象被分配在一個堆中,即用以表示一個大部分非結構化的內存區域。
這是在程序運行時須要給對new操做產生的對象分配存儲空間,是一個沒有特別限制的存儲空間。web
函數調用造成一個棧幀;
棧的特色:先進後出(First in, last out)函數執行棧過程;能夠當作是每次函數first運行時,將函數入棧,此時函數中的其餘運行函數(second函數)須要再次入棧,執行完該second函數以後,該second函數將會出棧,繼而完成first的執行,執行完成後,first將會出棧;api
隊列是一種和棧不同的數據結構,相似管道,先進入的將會現出來,和棧是相反的。promise
javascript從誕生之日起就是一門單線程的非阻塞的腳本語言。這是由其最初的用途來決定的:與瀏覽器交互。
單線程意味着,javascript代碼在執行的任什麼時候候,都只有一個主線程來處理全部的任務。
而非阻塞則是當代碼須要進行一項異步任務(沒法馬上返回結果,須要花必定時間才能返回的任務,如I/O事件)的時候,主線程會掛起(pending)這個任務,而後在異步任務返回結果的時候再根據必定規則去執行相應的回調。
單線程是必要的,也是javascript這門語言的基石,緣由之一在其最初也是最主要的執行環境——瀏覽器中,咱們須要進行各類各樣的dom操做。試想一下 若是javascript是多線程的,那麼當兩個線程同時對dom進行一項操做,例如一個向其添加事件,而另外一個刪除了這個dom,此時該如何處理呢?所以,爲了保證不會 發生相似於這個例子中的情景,javascript選擇只用一個主線程來執行代碼,這樣就保證了程序執行的一致性。
固然,現現在人們也意識到,單線程在保證了執行順序的同時也限制了javascript的效率,所以開發出了web worker技術。這項技術號稱讓javascript成爲一門多線程語言。
然而,使用web worker技術開的多線程有着諸多限制,例如:全部新線程都受主線程的徹底控制,不能獨立執行。這意味着這些「線程」 實際上應屬於主線程的子線程。另外,這些子線程並無執行I/O操做的權限,只能爲主線程分擔一些諸如計算等任務。因此嚴格來說這些線程並無完整的功能,也所以這項技術並不是改變了javascript語言的單線程本質。
能夠預見,將來的javascript也會一直是一門單線程的語言。
那麼爲了可以很好地提升的腳本的效率,故而設計的時候有一個很是有趣的特性是事件循環模型,與許多其餘語言不一樣,它永不阻塞。 處理 I/O 一般經過事件和回調來執行,因此當一個應用正等待IndexedDB查詢返回或者一個 XHR 請求返回時,它仍然能夠處理其它事情,如用戶輸入。瀏覽器
宏任務包括:script, setTimeout, setInterval, setImmediate, I/O,UI rendering,requestAnimationFrame
微任務包括:process.nextTick(node api), 原生Promise(有些實現的promise將then方法放到了宏任務中),Object.observe(已廢棄), MutationObserver服務器
全部的宏任務放在一個宏任務隊列(即任務隊列),處理完一個宏任務(從sccript開始),將微任務隊列(包含當時全部的微任務)壓入任務隊列(宏任務隊列)並執行,以後再取下一個任務隊列(宏任務)中的宏任務。
https://juejin.im/post/5b6d58...
https://github.com/ccforward/... 【函數執行棧和事件隊列】