一、核心線程
瀏覽器是一個多進程多線程的系統,它會有一個主進程進行任務的調度。有一個第三方插件進程,避免插件崩潰時影響頁面內容。然後每個 tab 頁面都是一個單獨的進程,每個 tab 進程中又有以下幾個重要的線程:
- GUI 渲染線程
- JS 引擎線程
- 定時器線程
- 異步請求線程
- 事件觸發線程
1.1 GUI 渲染線程
- 主要負責頁面的渲染,包括解析 HTML,CSS,構建 DOM 樹 -> CSS 規則樹 -> 渲染樹,最後計算元素的位置、渲染,迴流、重繪等
- 當執行 JS 引擎線程時,GUI 渲染線程會被掛起,當任務隊列空閒時,GUI 線程恢復執行
1.2 JS 引擎線程
- 該線程主要負責解析、執行 js 腳本
1.3 定時器線程
- 該線程負責執行定時器任務,如 setTimeout、setInterval(如果放在主線程計時,受限於 js 單線程,計時會不準)
- 主線程逐行執行代碼到異步任務時,會將定時器任務交給該線程處理。等到計時結束,事件觸發線程會將回調函數加入到任務隊列尾部,等待 JS 引擎線程執行
1.4 異步請求線程
- 負責執行異步請求,如 ajax、promise、http
- 主線程逐行執行代碼到異步請求時,會異步任務交給該線程處理。等到異步任務的狀態碼變更時,事件觸發線程會將回調函數加入到任務隊列尾部,等待 JS 引擎線程執行
1.5 事件觸發線程
主要負責將準備好的事件加入到任務隊列,如 setTimeout 回調、ajax 回調
二、事件循環 Event Loop
2.1 宏任務與微任務
先來道題
console.log('開始')
Promise.resolve().then(()=>{
console.log('Promise1')
setTimeout(()=>{
console.log('setTimeout2')
},0)
})
setTimeout(()=>{
console.log('setTimeout1')
Promise.resolve().then(()=>{
console.log('Promise2')
})
},0)
先揭曉答案
- 開始
- Promise1
- setTimeout1
- Promise2
- setTimeout2
這是視頻講解
JS 中的事件循環
這裏注意以下幾點即可
- 首先遇到同步任務,壓入執行棧
- 遇到微任務壓入微任務隊列
- 遇到宏任務壓入宏任務隊列
- 所有的同步任務相當於一個宏任務,而一個宏任務執行完成之後,需要把微任務隊列中的全部任務全部執行
三、Node 中的事件循環
先注意一點,不管是瀏覽器中的事件循環還是 node 中的事件循環,都不是 JS 引擎去實現的,而是瀏覽器和 node 自己實現的東西。
所以即使 chrome 和 node 都用了 V8 引擎,可是二者的事件循環卻完全不是一回事,實現的邏輯也是大相徑庭。
node 中的事件循環是在 libuv 中實現的,libuv 是一個聚焦於 I/O 的庫,一個基於事件驅動的跨平臺抽象層,封裝了不同系統底層一些特性,對外提供統一的 API。
node.js 的運行機制大致如下:
V8 引擎解析 js 代碼
調用 node API
libuv 負責執行 node API,