js的單線程
從我們第一天接觸js的時候我們就知道js是單線程的,且js是異步的,首先來看一下基本概念
什么是線程
線程是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。(百度百科)
舉個最簡單的例子 , 在我們電腦的任務管理器中你打開一個應用程序的時候 就會多一個進程,他代表了cpu能處理的單個任務。
線程是進程下的執行者,一個進程至少會開啟一個線程(主線程),也可以開啟多個線程,比如你打開了一個vscode編輯器,里邊你能做的操作會有很多比如cmd node 等等
瀏覽器的進程
js是運行在瀏覽器的,是由js引擎解析和執行的,那么我們先了解一下瀏覽器都有哪些線程
首先要說明的是 瀏覽器是多線程的
-
Browser進程:瀏覽器的主進程
-
第三方插件進程
-
GPU進程
-
瀏覽器渲染進程
對於前端來講這些進程中最重要的是瀏覽器渲染進程,也被稱之為是瀏覽器內核,因為我們的頁面渲染,事件等等都在這個進程中進行的
瀏覽器的渲染進程里邊有一些常駐的線程比如說
GUI渲染線程
- 負責解析html css 構建dom樹 編譯 RenderObject樹,計算布局渲染等等
- 在頁面重回或者dom回流的時候這個線程就會被執行
- 頁面在第一次渲染的時候肯定會觸發這個線程
Js引擎線程
- 主要負責處理javascript的腳本
- 也稱之為js內核,一個tab頁中只有一個js線程在運行js程序
需要注意的是Gui渲染線程和js引擎線程是沖突的,他們不能同時執行
所以說如果我們頁面在一開始js文件需要計算或者操作的時間比較長的時候,會出現大段時間的白屏,
因為js引擎在執行的時候會使頁面渲染堵塞,如果dom發生更新的時候,gui更新會被存在一個隊列里邊,等到js引擎空閑的時候才會被執行
一個tab頁里邊無論如何都只有一個js線程,就算是后來的webworker ,他也是只屬於js引擎的一個子類,並且它不能操作dom
事件觸發線程
- 常用的事件有很多比如點擊事件 ajax請求事件等等他們會在對應的條件觸發的時候被添加到事件線程中
- 這些事件會在js引擎空閑的時候去執行
定時觸發器線程
- 定時器與計時器的線程,在滿足條件之后會被添加到事件隊列里邊,等待js引擎空閑的時候去執行
http請求
- 在發送http請求的時候,這個線程會被執行
- 再有回調函數的時候,這個線程會把回調的事件放入到事件隊列中去,等待js引擎執行
突然覺得js引擎好累,什么都是要他去做,為什么不設計成多線程,從網上其他地方看了的答案,大同小異,講的是多線程操作dom有可能會同時操作一個dom發生錯誤雲雲

說完了上邊的一些概念,你也應該大體的了解了js的單線程這個問題
下邊從js引擎的一些運行機制說一說js的異步
剛才已經講了,瀏覽器中不僅有js引擎線程還有其他,異步的話主要會用到事件觸發線程和定是觸發器線程的概念
在js中氛圍同步任務和異步任務 ,所有的同步任務都會在主線程上執行,形成一個執行棧(先進后出)
在主線程之外還有一個異步的事件隊列
在執行棧執行完畢之后,也就是同步任務執行完畢之后,js引擎線程就會去輪詢事件隊列,看有沒有需要執行的事件,有的話就會執行事件隊列的事件
我記得我最開始的時候使用ajax 或者數據庫查詢的時候遇到過這么一個情況,在做完操作之后就天真的認為可以用返回的值,結果當時我被異步搞得里焦外嫩,甚至我以為在發送完請求之后多做一些操作等一會請求,請求值就會返回來,結果。。。
其實不然, 在同一個事件循環內部,無論做多少操作,你的異步操作只會在執行棧執行完畢之后才會被執行
同時異步也是有優先級的,在事件循環里邊js的任務類型分為兩種
- 宏任務
- 微任務
宏任務就是說執行棧里的每一個被執行的代碼就是一個宏任務,包括一個事件產生的回調執行
宏任務會在執行完畢一段代碼之后先對dom進行一次渲染,然后再執行下一個宏任務
微任務是再宏任務執行完畢之后立即執行的,他在dom重新渲染之前,
所以微任務的相應速度比宏任務是要快的
像 定時器,延時器 主代碼塊等等就是一個宏任務,promise nextTick等就是微任務
正常在延時器打印和在promise里邊打印,promise的打印是會比延時器的先打印的
所以總結一下js的運行機制是
- 執行棧執行宏任務
- 執行棧沒有任務就去輪詢事件隊列
- 如果執行期間遇到微任務,就添加到微任務隊列
- 一個宏任務執行完畢后會立即執行當前微任務隊列的任務
- 宏任務執行完畢后開始渲染
- 然后開啟下一輪宏任務

以上有說的不對或者不足之處,請批評指正
