1.瀏覽器的進程模型
1.何為進程
程序運行需要有它自己專屬的內存空間,可以把這塊內存空間簡單的理解為進程。每個應用至少得有一個進程,進程之間相互獨立,即使要通信,也需要雙方通信。
2.何為線程
有了進程后,就可以運行程序的代碼了。運行代碼的人稱之為線程,一個進程至少需要一個線程,所以在進程開啟后會自動創建一個線程來運行代碼,該線程稱之為主線程。如果程序需要同時運行多塊代碼,主線程就會啟動更多的線程來執行代碼,所以一塊進程中可以包含多個線程。多個線程共享內存空間。
3.瀏覽器有那些進程和線程
瀏覽器是一個多進程多線程應用程序。瀏覽器內部工作極其復雜。為了避免相互影響,為了減少連環崩潰的幾率,當啟動瀏覽器后,它會自動啟動多個進程。
瀏覽器進程:負責標簽頁、導航欄、用戶交互鼠標點擊、滾理滑動、子進程管理等。瀏覽器進程內部會啟動多個線程處理不同任務。
網絡進程:負責加載網絡資源。網絡進程內部會啟動多個線程處理不同網絡任務。
渲染進程:一個標簽頁一個渲染進程。渲染進程啟動后會開啟一個渲染主線程,主線程負責HTML,CSS,JS代碼。默認情況下,瀏覽器會為每個標簽頁開啟一個新的渲染進程,以保證不同標簽頁之間不相互影響。
2.渲染主線程是如何工作的?
1.渲染主線程處理的任務包括但不限:
- 解析HTML CSS;
- 計算樣式;
- 布局;
- 處理圖層;
- 每秒把頁面畫60次;
- 執行全局JS代碼;
- 執行事件處理函數;
- 執行計時器回調函數;
- ....
疑問:為何渲染進程不適用多個線程來處理這些事情?
- 競態條件:多線程處理會導致訪問共享內存的競爭條件,可能導致數據不一致和死鎖等問題。
- 同步問題:多線程需要進行同步,避免數據競爭和死鎖,這會增加代碼的復雜度和開銷。
- 安全問題:多線程可能會存在安全漏洞,如數據泄露、內存溢出等問題。
- 性能問題:多線程處理可能會導致過多的上下文切換和內存消耗,從而降低程序的性能和穩定性。
渲染主線程:正在執行任務1
消息隊列:任務2,任務3,任務4。。。。。
其他線程:任務5↑,任務6↑
1.最開始的時候,瀏覽器會進入無限循環。
2.每一次循環會檢查消息隊列中是否有任務存在。如果有,就取出第一個任務執行,執行完一個后進入下一次循環;如果沒有,則進入休眠狀態。
3.其他所有線程(包括其他進程的線程)可以隨時向消息隊列添加任務。新任務會添加到消息隊列的末尾。在添加新任務時,如果主線程是休眠狀態,則會將其喚醒以繼續循環拿取任務。
以上過程,被稱為事件循環。
2.何為異步?
代碼在執行過程中,會遇到一些無法立即處理的任務,比如:
- 計時完成后需要執行的任務 -- setTimeout、setInterval
- 網絡通信完成后需要執行的任務 --XHR、Fetch
- 用戶操作后需要執行的任務 --addEventListener
如果讓渲染主線程等待這些任務的時機到達,就會導致主線程長期處于阻塞狀態,導致瀏覽器卡死。
3.JS為何會阻塞渲染?
4.任務有優先級嗎?
任務沒有優先級,在消息隊列中先進先出
但是消息隊列是有優先級的:W3C最新解釋,(很多消息隊列)每一個任務都有一個任務類型,同一個類型的任務必須在一個隊列,不同類型的任務可以分屬于不同的隊列。在一次事件循環中,瀏覽器可以根據實際情況從不同隊列中取任務。
瀏覽器必須準備好一個微隊列,微隊列中的任務優先所有其他任務執行。W3C最新拋棄了宏隊列說法。
在chorme的實現中,至少包含了下面的隊列:
- 延時隊列:用于存放計時器到達后的回調任務,優先級【中】
- 交互隊列:用于存放操作后產生后的事件處理任務,優先級【高】
- 微隊列:用戶存放需要最快執行的任務,優先級【最高】
添加任務到微隊列的方式:Promise,MutationObserver 例如:Promise.resolve().then(fn)
面試題:
解釋一下JS異步:
JS是一門單線程語言,這是因為他運行在瀏覽器的渲染主線程中,而渲染主線程只有一個。渲染主線程承擔了很多工作,渲染頁面、執行、JS都在其中運行。如果使用同步的方式,就極有可能導致主線程產生阻塞,從而導致消息隊列中很多其他任務無法得到執行。這樣會導致繁忙的主線程白白消耗時間,另一個方面無法及時更新,給用戶造成卡死現象。所以瀏覽器采用異步的方式來避免。具體做法是當某些任務發生時,比如計時器、網絡、事件監聽,主線程將任務交給其他線程去處理,自身立即結束任務的執行,轉而去執行后續代碼。當其他線程完成時,將事先傳遞的回調函數包裝成任務,加入到消息隊列的末尾排隊,等待主線程調度執行。在這種異步模式下,瀏覽器永不阻塞,從而最大限度的保證了單線程的運行。
闡述一下JS的事件循環:
事件循環又叫消息循環,是瀏覽器的工作方式。在chrome的源碼中,它開啟一個不會結束的for循環,每次循環從消息隊列中取出第一個任務執行,而其他線程只需要在合適的時候將任務加入到隊列的末尾即可。根據最新W3C的解釋,消息隊列已經不能簡單分為宏隊列和微隊列,而是分為延時隊列,交互隊列和微隊列。不同的任務可以屬于不同隊列,不同任務隊列有不同優先級,在一次事件循環中,有瀏覽器自行決定取哪一個隊列的任務。但瀏覽器必須要一個微隊列,微隊列的任務一定具有最高的優先級,必須優先調度。
JS中的計時器能做到精確計時嗎?為什么?
不行,因為計算機硬件沒有原子鐘,無法做到精確計時;
操作系統的計時函數本身具有少量偏差,由于JS的計時器最終調用的是操作系統的函數,也就攜帶這些偏差;
按照W3C標準,瀏覽器實現計時器時,如果嵌套層級超過5層,計時器間隔最少4ms
受到時間循環的影響,計時器的回調函數只能在主線程空閑時運行,因此又帶來誤差。
浙公網安備 33010602011771號