JS引擎中的執行緒,事件迴圈,上下文

2023-06-08 21:00:50
 
執行緒
瀏覽器中有哪些程序呢?
1.瀏覽器程序:瀏覽器的主程序,負責瀏覽器的介面介面顯示,與使用者互動,網址欄輸入、前進、後退,以及頁面的建立和銷燬。
2.渲染程序(瀏覽器核心):預設一個tab頁面一個渲染程序,主要的作用為頁面渲染,指令碼執行,事件處理等。
3.GPU程序:用於3D繪製等,將開啟了3D繪製的元素的渲染由CPU轉向GPU,也就是開啟GPU加速。
4.網路程序:主要負責頁面的網路資源載入。
5.外掛程序:每種型別的外掛對應一個程序,僅當使用該外掛時才建立。
6.音訊程序:瀏覽器音訊管理。
 
瀏覽器核心就是在渲染程序中,裡面包含了多個引擎,它們擁有各自的執行緒。
GUI渲染執行緒
負責解析html, css。構建DOM樹和RenderObject樹。佈局和繪製。
當頁面元素中的某個dom節點有變化,需要繪製時,此執行緒就會執行。

JS引擎執行緒
它裡面有個event loop和一個事件佇列。這2者是JS引擎的核心基礎。
擁有非同步處理能力,JS引擎是單執行緒但可以實現非同步並行處理事件,實現非同步的基礎是依靠上面的event loop和事件佇列。H5的 Web Worker 標準規定,允許 JavaScript 指令碼建立多個執行緒,但是子執行緒完全受主執行緒控制,且不得操作 DOM。所以,這個新標準並沒有改變 JavaScript 單執行緒的本質。
它與GUI渲染執行緒互斥,用於處理頁面互動和DOM更新,所以JS引擎執行緒和GUI渲染執行緒在處理任務時必須保證按順序序列執行,一個執行另一個就要等待,這樣才能保證每次頁面渲染,更新都是確定的,不存在衝突。所以如果js處理的事件耗時,頁面就會出現卡頓。
JS引擎主要負責處理JS指令碼的詞法分析,語法分拆,生成語法樹,程式碼執行。記憶體管理,垃圾回收。

事件觸發執行緒
監控各種事件的發生,比如使用者的互動事件、頁面DOM渲染完成事件等。當這些事件發生時,事件觸發執行緒會將回撥事件從定時器觸發執行緒或者非同步HTTP請求執行緒它們的事件佇列中讀取出來,放置到JS引擎的事件佇列中,等待JavaScript引擎的執行。
輔助JS引擎執行緒管理事件佇列,它和JS引擎執行緒是平級的關係。它的管理事件佇列體現在將要處理的事件從其他執行緒佇列中取出放到JS引擎的事件佇列中。

定時器觸發執行緒
用於專門做定時器計數用的,如果其他執行緒做這件事,可能會因為執行耗時任務而錯過按時觸發。
觸發時機是當js引擎執行到setTimeOut, setInternal時,就會呼叫對應的webAPI,這些系統API內部都是將任務放置到定時器觸發執行緒中進行處理,線上程中會把定時器事件以key:value的形式儲存在event table中 方法名:回撥函數 ,當回撥時間到時,就將回撥函數放置在自己的事件佇列中,並行通知到事件觸發執行緒,讓事件觸發執行緒將
事件放置到JS引擎的事件佇列中。

非同步HTTP請求執行緒
主要用於檢測網路狀態變化,如果檢測到網路成功回撥,則會把儲存在自己event table中的回撥事件放置到當前執行緒自己的事件佇列中,然後通知,則會通知時間觸發執行緒把處理回撥函數放置到事件佇列中。,並行通知到事件觸發執行緒,讓事件觸發執行緒將
事件放置到JS引擎的事件佇列中。
觸發時機是當js引擎執行到ajax請求時,則會呼叫webAPI提供的介面,介面內部就會切換到非同步HTTP請求執行緒中做網路事件的處理。
 
事件迴圈
 
事件迴圈主要解決了哪些問題?
1.避免耗時任務阻塞流程
html頁面的載入是序列的,渲染程序解析HTML頁面檔案,碰到頁面標籤和css時,交給GUI渲染執行緒處理,碰到js標籤時,則停止GUI渲染執行緒開始讓js引擎處理js任務,如果要等耗時任務執行完再執行下面的任務,則卡主了頁面。
2.增加了CPU執行效率
當JS引擎執行時碰到setTimeOut定時器事件,ajax網路事件時,都會呼叫webAPI讓系統在專門的子執行緒進行處理,等到了回到時間,再把回撥任務新增到js引擎的任務佇列中,實現了耗時任務的同步執行。序列回撥,增加了執行效率。

JS引擎的程式碼執行流程
1.JS引擎開始執行程式碼。
2.根據程式碼中的async,sync 同步、非同步標識確定走同步流程還是非同步流程。
3.同步流程是執行在JS引擎執行緒的,非同步流程則是使用單獨的執行緒進行處理,等處理完了將事件回撥放置到JS引擎的事件佇列中,讓JS引擎進行執行。
4.當JS引擎執行完任務後會檢查其任務佇列中是否還有待處理的任務,如果有就繼續出來,如果沒有就停止等待。
js引擎存在一個monitoring process程序,它會持續不斷的檢查主執行緒執行棧是否為空,一旦檢測到有任務就通知JS引擎取出任務執行。
任務的執行是按照任務進入佇列的順序進行的。任務的執行時間是受上一個任務的執行時長決定的,就像setTimeOut,方法裡設定的是2秒後將回撥放入js引擎的事件佇列,但是任務的具體執行時間是不能確定的。

 

宏任務與微任務
setTimeOut, setInterval, async這樣的是宏任務
progress.nextTick, Promise這樣的是微任務,微任務是在執行宏任務的時候產生的,它存在全域性上下文中。
宏任務與微任務的執行順序是:宏任務 -> 微任務 -> 宏任務 -> 微任務 -> 空。
先執行宏任務,執行結束後。檢視微任務佇列中是否有微任務,有的話就執行,如果在執行微任務的過程中又產生了微任務,那麼這個新的微任務會直接新增到當前執行的微任務佇列。直到微任務佇列中的任務都執行完才會開啟下一輪宏任務->微任務迴圈。

 

JS執行上下文
 
JS執行上下文是JS程式碼執行的環境,JS程式碼的執行一定要在其執行上下文中。
全域性執行上下文:瀏覽器中的全域性上下文就是window, window是一個物件,全域性上下文中的this指向的就是window。
函數執行上下文:每次函數的呼叫都會產生一個函數上下文,函數上下文內容包括:引數,區域性變數的棧空間,堆空間。
上下文的生命週期如下:

 

上下文是一個物件,裡面包含了變數物件VO,作用域鏈scopeChain,this物件。其基本結構如下:
ExecutionContext={
  scopeChain:{},
  VO:{},
  this:{}
}
全域性上下文變數物件
globalEC={
   VO:window,
   scopeChain:{},
   this:window
}
函數上下文變數物件
function a(a,b){}
a(1,2)
innerTestEC={
  VO={
    arguments:{0:1,1:2,length:2}
    a:undefined,
    b:undefined
    },// 變數物件
  scopeChain:[VO(innerTest),VO(test),VO(global)],//作用域鏈
  this:{}
}

 


參考文章:
https://juejin.cn/post/6844903512845860872
https://zhuanlan.zhihu.com/p/492563097