JavaScript的單線程你真的理解了嗎

WBOY
發布: 2022-01-10 19:08:01
轉載
2297 人瀏覽過

這篇文章為大家帶來了我們的JavaScript單執行緒的相關知識,JavaScript是一門單執行緒的語言,為什麼JavaScript可以一邊執行定時器一邊執行函數,希望對大家有幫助。

JavaScript的單線程你真的理解了嗎

1. 行程與執行緒

1.1 行程(Process)

是電腦中的程式關於某資料集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是作業系統結構的基礎。在當代面向執行緒設計的電腦結構中,進程是執行緒的容器。程序是指令、資料及其組織形式的描述,流程是程式的實體。是電腦中的程式關於某資料集合上的一次運行活動,是系統進行資源分配與調度的基本單位,是作業系統結構的基礎。程序是指令、資料及其組織形式的描述,過程是程式的實體。

我們在這裡將進程比喻為工廠的車間,它代表CPU所能處理的單一任務。任一時刻,CPU總是運行一個進程,其他進程處於非運行狀態。

1.2 執行緒(thread)

是作業系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以並發多個線程,每個線程並行執行不同的任務。

這裡把線程比喻一個車間的工人,即一個車間可以允許由多個工人協同完成一個任務。

2. 多線程的瀏覽器

瀏覽器核心是多線程,在核心控制下各線程相互配合以保持同步,一個瀏覽器通常由以下常駐執行緒組成:

  • GUI 渲染執行緒

  • #JavaScript引擎執行緒

  • ##事件觸發線程

  • 定時觸發器執行緒

  • 非同步http請求執行緒

JavaScript的單線程你真的理解了嗎

我們看到了JS引擎線程,非常的熟悉,沒有錯,這裡是我們執行javascript腳本程式的地方。

而JS引擎是多執行緒的,單執行緒是指JS引擎執行JS時只分了一個執行緒給他執行,意思是JS引擎分配了一個執行緒給JavaScript執行,也就是我們所說的單線程。

2.1 這裡再說下JS的執行機制

由於JavaScript是單執行緒(一個Tab頁內中無論何時都只有一個JS執行緒在執行JavaScript程式) 。

所以我們需要依賴任務佇列來進行JavaScript程式碼的執行。

JS引擎會一直等待著任務佇列中任務的到來,然後執行任務。

同步任務這麼執行當然沒問題,我們把任務都放在任務佇列裡,一個一個執行,邏輯很清晰。但是,如果我們向後台發送請求,發送加接收這段時間可能需要一秒,我們不能等它一秒吧,如果請求五次,那就等五秒?顯示不符合我們的需求,所以,我們需要非同步任務來處理這個問題。

2.2 同步任務和非同步任務

同步任務是指在主執行緒上排隊執行的任務,只有前一個任務執行完畢,才能繼續執行下一個任務,當我們開啟網站時,網站的渲染過程,例如元素的渲染,其實就是一個同步任務

非同步任務是指不進入主線程,而進入任務佇列的任務,只有任務佇列通知主線程,某個非同步任務可以執行了,該任務才會進入主線程,當我們打開網站時,像圖片的加載,音樂的加載,其實就是一個非同步任務。

大家一定對Event Loop有比較具象的認知,這邊我不詳細說了,不懂可以跟我說,我再說就是了。

3. 本文重重點--可直接看

但是,大家有沒有對任務佇列抱持疑問?這是個對象?是個數組?照我的邏輯來說,我們JavaScript主執行緒執行同步函數,非同步函數可以放在任務佇列裡,這個任務佇列可以是個對象,當我們執行完同步任務的時候,把這個物件(任務佇列)壓進主線程中就可以了,但是事實並不我想的這樣的。

Evnet Loop的任務佇列放在了瀏覽器的事件觸發線程中,當JS引擎執行非同步函數的時候,會將非同步任務放在事件觸發線程中,當對應的非同步任務符合觸發條件被觸發時,事件觸發線程會把非同步任務加到JS引擎中的主線程的隊尾,等待執行。

是不是跟我們想像的JavaScript單執行緒不太一樣?好吧,確實不太一樣,所以最後的結論是,我們所說的任務隊列竟然是一個線程。

然後,說回我們開頭剛開始說過的定時器,大家基本上也能猜出來了,它是由定時器線程控制的。

因為JavaScript是單執行緒的, 如果處於阻塞執行緒狀態就會影響記計時的準確,因此很有必要單獨開一個執行緒用來計時。

當使用setTimeout或setInterval時,它需要計時器執行緒計時,計時完成後就會將特定的事件推入事件佇列中。

4. 結論

所以說,我們說JavaScript是單線程的沒錯,就是天王老子來了它也是單線程的,但是我們的Event Loop和定時器是放在其他執行緒的。

5. V8引擎--擴展

V8引擎是一個JavaScript引擎實現,最初由一些語言方面專家設計,後來被Google收購,隨後谷歌對其進行了開源。

V8使用C 開發,在執行JavaScript之前,比較其它的JavaScript的引擎轉換成字節碼或解釋執行,V8將其編譯成原生機器碼(IA-32, x86-64, ARM , or MIPS CPUs),並且使用瞭如內聯緩存(inline caching)等方法來提高效能。

有了這些功能,JavaScript程式在V8引擎下的運行速度媲美二進位程式。 V8支援眾多作業系統,如windows、linux、android等,也支援其他硬體架構,如IA32,X64,ARM等,具有很好的可移植和跨平台特性。

5.1 工作流程

V8引擎在執行JavaScript的過程中,主要有兩個階段:編譯和運行,與C 的執行前完全編譯不同的是,JavaScript需要在使用者使用時完成編譯和執行。在V8中,JavaScript相關程式碼並非一下完成編譯的,而是在某些程式碼需要執行時,才會進行編譯,這就提高了回應時間,減少了時間開銷。在V8引擎中,原始碼先被解析器轉變為抽象語法樹(AST),然後使用JIT編譯器的全代碼產生器從AST直接產生本地可執行程式碼。這個過程不同於JAVA先生成字節碼或中間表示,減少了AST到字節碼的轉換時間,提高了程式碼的執行速度。但由於缺少了轉換為字節碼這一中間過程,也就減少了優化程式碼的機會。

V8引擎編譯本機程式碼時使用的主要類別如下所示:

  • #Script:表示JavaScript程式碼,即包含原始程式碼,又包含編譯之後產生的本機程式碼,也就是編譯入口,又是運行入口;

  • Compiler:編譯器類,輔組Script類來編譯生成程式碼,呼叫解釋器(Parser)來產生AST和全程式碼產生器,將AST轉變為本地程式碼;

  • AstNode:抽象語法樹節點類,是其他所有節點的基類,包含非常多的子類,後面會針對不同的子類別產生不同的本機程式碼;

  • AstVisitor:抽象語法樹的訪客類,主要用來遍歷異質的抽象語法樹;

  • FullCodeGenerator:AstVisitor類別的子類,透過遍歷AST來為JavaScript產生本機執行程式碼。

JavaScript的單線程你真的理解了嗎

JavaScript程式碼編譯的過程大致為:Script類別呼叫Compiler類別的Compile函數為其產生本機程式碼。 Compile函數先使用Parser類別產生AST,再使用FullCodeGenerator類別來產生本機程式碼。本機程式碼與特定的硬體平台密切相關,FullCodeGenerator使用多個後端來產生與平台相符的本機彙編程式碼。由於FullCodeGenerator透過遍歷AST來為每個節點產生對應的彙編程式碼,缺少了全域視圖,節點之間的最佳化也就無從談起

【相關推薦:javascript學習教學

以上是JavaScript的單線程你真的理解了嗎的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:juejin.im
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!