最細最有條理解析:事件循環(消息循環)是什么?進程與線程的定義、關系與差異

目錄

事件循環:引入

一、瀏覽器的進程模型

1.1、什么是進程(Process)

1.2、什么是線程(Thread)

1.3、進程與線程之間的關系聯系與區別

二、瀏覽器有哪些進程和線程

2.1、瀏覽器的主要進程

①瀏覽器進程

②網絡進程

③渲染進程

2.2、渲染主線程的工作原理

渲染主線程的消息隊列

三、事件循環

3.1、什么是事件循環(Event Loop)

3.2、渲染主線程的事件循環如何確定任務的優先級??

3.3、事件循環的執行示例

示例一

示例二

測試題:如下代碼塊執行后輸出順序是什么?

四、相關問題

4.1、為什么要使用事件循環

4.2、如何理解JS的異步

4.3、JS中計時器能精確計時嗎,為什么?

五、總結與相關資源


事件循環(消息循環):引入

????????事件循環是瀏覽器的核心內容。

????????與計時器、Promise、ajax、node等技術有關。

????????要想說清楚事件循環,必須先聊進程與線程。

一、瀏覽器的進程模型

1.1、什么是進程(Process)

? ? ? ? 我們先看看定義:?

  • 進程是程序的執行實例。它是操作系統進行資源分配和調度的一個獨立單位。
  • 進程擁有獨立的內存空間,可以擁有或分配不同的資源如CPU時間、文件、消息隊列等。
  • 進程可以創建子進程,形成進程樹結構。

? ? ? ? 對于coder來說,說到實例肯定不陌生,一個程序的運行就至少需要產生一個實例,實例負責給程勛運行提供運行所需的資源。

????????簡單的說,程序運行需要它專屬的內存空間(RAM和虛擬內存),這部分內存空間可以簡單的理解為該程序對應的進程。

????????每個應用至少有一個進程,且相互獨立,即使要通信,也要雙方同意。

1.2、什么是線程(Thread)

? ? ? ? 先看定義:

  • 線程是程序執行的邏輯單元,是程序中一個單一的順序控制流程。
  • 在一個進程中可以包含多個線程,它們共享進程的資源,如內存空間,但每個線程有自己的線程棧和程序計數器。

? ? ? ? 簡單的說,線程是進程的執行者。一個進程可以有多個線程,線程之間資源共享, 通信簡單,獨立執行,開銷較小。

????????一個進程至少有一個線程,所以進程開啟后就會自動創建一個線程來運行代碼,該線程稱之為主線程。

????????如果程序需要同時執行多塊代碼,主線程就會啟動更多的線程來執行代碼,所以一個進程中可以包含多個線程。重要的事情要多次重復,這些線程資源共享, 通信簡單,獨立執行,開銷較小(線程相比于進程)!

1.3、進程與線程之間的關系聯系與區別

? ? ? ? 綜上所述,二者之間的聯系與區別就很明確了:

  1. 進程是程序某一部分或整體的運行實例,每個程序運行都至少需要一個進程。(但不一定只有一個,為了保證程序的穩定性,往往會有多個進程,一個進程崩潰不會導致整個程序崩潰)
  2. 線程是進程的執行者,每個進程都至少包含一個線程(即主線程)。
  3. 線程是操作系統能夠進行運算調度的最小單位,它被包含在進程之中。因此同一進程內的線程可以共享進程的資源,如全局變量、文件句柄等。
  4. 與創建新進程相比,創建線程的開銷較小,因為線程可以復用進程的資源。
  5. ?由于線程共享同一地址空間,線程間的通信更簡單,不需要復雜的進程間通信機制。

????????關系示意圖:

二、瀏覽器有哪些進程和線程

????????首先,瀏覽器是一個多進程多線程的應用程序。

2.1、瀏覽器的主要進程

????????瀏覽器內部工作極其復雜,為了避免相互影響,為了減少連環崩潰的幾率,當啟動瀏覽器后,它會自動啟動多個進程。一般主要有瀏覽器進程、網絡進程、渲染進程

????????可以在瀏覽器的任務管理器中查看當前的所有進程。

①瀏覽器進程

????????主要負責界?顯示、?戶交互、子進程管理等。瀏覽器進程內部會啟動多個線程處理不同的任務。

②網絡進程

????????負責加載網絡資源。網絡進程內部會啟動多個線程來處理不同的?絡任務。

③渲染進程

????????渲染進程啟動后,會開啟?個渲染主線程主線程負責執行 HTML、CSS、JS 代碼

????????默認情況下,瀏覽器會為每個標簽頁開啟?個新的渲染進程,以保證不同的標簽頁之間不相互影響。

2.2、渲染主線程的工作原理

????????渲染主線程是瀏覽器中最繁忙的線程,也是前端開發中提高運行效率需要著重關注的線程,需要它處理的任務包括不限于:解析HTML、解析CSS、計算樣式、布局、處理圖層、每秒60次渲染,執行全局JS代碼、執行事件處理函數、執行計時器回調函數等。

? ? ? ? 那渲染主線程如何執行和調度這些任務呢?總要有個章法去有序執行這些步驟,同時兼顧這些步驟的因果順序和中途插入的步驟。

????????比如任務之間存在因果順序:不解析HTML、CSS,就沒辦法執行布局任務。

? ? ? ? 又比如任務之間會有插入情況:執行JS函數的過程中,用戶點擊了某個按鈕或者計時器到了時間需要執行回調函數。

? ? ? ? 這里就引入了一個概念:

? ? ? ? 渲染主線程的消息隊列
  1. 在最開始的時候,渲染主線程會進入一個無限循環
  2. 每一次循環會檢查消息隊列中是否有任務存在。如果有,就取出第一個任務執行,執行完一個后進入下一次循環;如果沒有,則進入休眠狀態。
  3. 其他所有線程(包括其他進程的線程)可以隨時向消息隊列添加任務。新任務會加到消息隊列的末尾。在添加新任務時,如果主線程是休眠狀態,則會將其喚醒以繼續循環拿取任務

? ? ? ? 簡而言之,渲染主線程的消息隊列就是渲染主線程的任務管家,負責給渲染主線程要執行的任務進行排序、管理、調度。渲染主線程只需要一直檢查消息隊列里面有沒有任務,按序執行即可,但消息隊列要考慮的可就多了(bushi)

? ? ? ? 這樣一來,就可以讓每個任務有條不紊的、持續的進行下去了。現在就能引出本文的核心內容:事件循環。

三、事件循環

3.1、什么是事件循環(Event Loop)

????????又稱消息循環(Message Loop),在有些情景也叫 Run Loop。

????????一言以蔽之:事件循環就是渲染主線程不斷循環不斷從消息隊列中讀取事件并執行的過程。

? ? ? ? 也可以說:事件循環又叫做消息循環,是瀏覽器渲染主線程的工作方式。(但并非只有瀏覽器的渲染主線程會進行事件循環,有時候網絡線程也會)

? ? ? ? 不是所有的線程都有事件循環,但是渲染主線程一般都有。

3.2、渲染主線程的事件循環如何確定任務的優先級??

? ? ? ? 首先,任務本身沒有優先級,消息隊列遵守先進先出的規則。

? ? ? ? 但是消息隊列有優先級。消息隊列一般至少由三個隊列:微隊列、交互隊列、延時隊列構成,其分類和優先級規則如下:

? ? ? ? 微隊列 > 交互隊列 > 延時隊列

  • 微隊列:用戶存放需要最快執行的任務(一般由Promise和MutationObserver生成),優先級「最高」
  • 交互隊列:用于存放用戶操作后產生的事件處理任務,優先級「高」
  • 延時隊列:用于存放計時器到達后的回調任務,優先級「中」

????????這里的優先級是指事件循環過程中,高優先級的隊列會“插隊”放入隊列。比如現在隊列中微隊列和延時隊列各有一個事件,先讀取微隊列中的任務,執行后又產生了一個微隊列任務和一個交互隊列任務,那么下一個執行的是新產生的微隊列任務,然后是新產生的交互隊列任務,最后才是一開始的延時隊列任務。

????????如下圖所示,消息隊列大概是個這樣的模型,只有微隊列完全空掉才會執行交互隊列中的任務,在同一類型的隊列中才嚴格遵守“先進先出”的隊列規則:

3.3、事件循環的執行示例

????????請問如下幾個例子的輸出順序是什么?

示例一
setTimeout(function () {console.log(1);
}, 0);function delay(duration) {var start = Date.now();while (Date.now() - start < duration) {}
}
delay(3000);
console.log(2);// 輸出順序為 2 、 1

(點擊代碼詳情查看答案)

? ? ? ? 解析:整體先作為一個任務①順序執行。setTimeout生成一個新任務②,放到延時隊列中(雖然計時為0,但是任務①還沒執行完畢,所以哪怕計時到了也只能在隊列等候執行)。delay函數將渲染主線程阻塞3秒,然后輸出2,任務①執行完畢,通過事件循環執行任務②,輸出1。

示例二
function a() {console.log(1);Promise.resolve().then(function () {console.log(2);});
}
setTimeout(function () {console.log(3);
}, 0);Promise.resolve().then(a);console.log(5);// 輸出順序為: 5 、 1 、 2 、 3

(點擊代碼詳情查看答案)

????????解析:整體作為任務①執行。setTimeout生成一個新任務②,放到延時隊列中。Promise生成一個新任務③(執行a函數),放到微隊列中。然后輸出5,任務①執行完畢。

? ? ? ? 此時消息隊列中微隊列有任務③,優先執行,先輸出1,然后Promise生成一個新任務④,放到微隊列中,任務③執行完畢。

? ? ? ? 此時微隊列又有任務④,優先執行,輸出2。任務④執行完畢。

? ? ? ? 此時消息隊列中微隊列和交互隊列為空,執行延時隊列中的任務②,輸出3,任務②執行完畢。

? ? ? ? 即輸出結果為:5 1 2 3。

測試題:如下代碼塊執行后輸出順序是什么?
function a() {console.log(1);Promise.resolve().then(function () {console.log(2);});
}setTimeout(function () {console.log(3);Promise.resolve().then(a)
}, 0);Promise.resolve().then(function(){console.log(4);
});console.log(5);// 輸出順序為: 5 、 4 、 3 、 1 、 2

四、相關問題

4.1、為什么要使用事件循環

????????在本文2.2中提到“讓每個任務有條不紊的、持續的進行下去”。那么為什么不使用事件循環就會出現問題?為什么“執行JS函數的過程中,用戶點擊了某個按鈕或者計時器到了時間需要執行回調函數”就會有矛盾?這兩個任務又沒有因果關系,直接一起執行不行嗎?

? ? ? ? 事實上,JS是一門單線程語言,這是因為它運行在瀏覽器的渲染主線程中,而渲染主線程只有一個。也就是說,JS函數不能多個一起進行,哪怕兩個任務相互獨立,也要有個規定來調度任務,有序執行。所以必須要有一個像事件循環一樣的邏輯來管理、調度任務。

4.2、如何理解JS的異步

????????代碼在執行過程中,會遇到一些無法立即處理的任務,比如:

  • 計時完成后需要執行的任務 —— setTimeout、setInterval
  • 網絡通信完成后需要執行的任務 —— XHR、Fetch
  • 用戶操作后需要執行的任務 —— addEventListener

????????如果讓渲染主線程等待這些任務的時機達到,就會導致渲染主線程長期處于“阻塞”的狀態,從而讓用戶感覺瀏覽器“卡死”,讓用戶的體驗變差。

????????因此,瀏覽器使用異步來解決這個問題。

????????具體做法是當某些任務發生時,比如計時器、網絡、事件監聽,主線程將任務交給其他線程去處理,自身立即結束任務的執行,轉而執行后續代碼。當其他線程完成時,將事先傳遞的回調函數包裝成任務,加入到消息隊列的末尾排隊,等待主線程調度執行。

????????從而最大程度的保證單線程的流暢運行。

4.3、JS中計時器能精確計時嗎,為什么?

? ? ? ? 不可以。原因如下:

? ? ? ? 從硬件角度來說:JS計時器是調用了操作系統中的計時函數,該函數本身就有少量偏差,硬件精度有限。

? ? ? ? 從語法標準上說:W3C標準中建議瀏覽器的計時器嵌套層級超過5層,則存在至少4ms的最少事件,這樣也會帶來偏差。

// 例如嵌套的層數小于等于5層,那么就會按照設置的時間執行。
setTimeout(function () {setTimeout(function () {setTimeout(function () {setTimeout(function () {setTimeout(function () {}, 0);}, 0);}, 0);}, 0);
}, 0);// 假如嵌套的層數大于5層,即使設置了0毫秒的間隔,瀏覽器也會確保至少有4毫秒的延遲,以避免潛在的性能問題,即:setTimeout(function () {setTimeout(function () {setTimeout(function () {setTimeout(function () {setTimeout(function () {setTimeout(function () {}, 0);}, 0);}, 0);}, 0);}, 0);}, 0);// 實際執行效果:setTimeout(function () {setTimeout(function () {setTimeout(function () {setTimeout(function () {setTimeout(function () {setTimeout(function () {}, 4);}, 4);}, 4);}, 4);}, 4);}, 4);

? ? ? ? 從事件循環的邏輯上講,計時器的回調函數只能在主線程空閑時進行,并不一定能在計時完成后立馬開始執行邏輯。

? ? ? ? 綜上所述,JS中計時器做不到精確計時。

五、總結與相關資源

? ? ? ? 度一教育的袁進老師談到他的理解:單線程是異步產生的原因,事件循環是異步的實現方式。

? ? ? ? 本質是因為渲染進程因為計算機圖形學的限制,只能是單線程。所以需要“異步”這個技術思想來解決頁面阻塞的問題,而“事件循環”是實現“異步”這個技術思想的最主要的技術手段。

????????但事件循環并不是全部的技術手段,比如Promise,雖然受事件循環管理,但是如果沒有事件循環,單一Promise依然能實現異步不是嗎?

????????博客不應該只有代碼和解決方案,重點應該在于給出解決方案的同時分享思維模式,只有思維才能可持續地解決問題,只有思維才是真正值得學習和分享的核心要素。如果這篇博客能給您帶來一點幫助,麻煩您點個贊支持一下,還可以收藏起來以備不時之需,有疑問和錯誤歡迎在評論區指出~

????????更多優質內容,請關注:

JS底層邏輯:

???? ? ?路由通配符,小小的字符有大大的作用,你真的熟悉嗎??

????????管理數據必備!偵聽器watch用法詳解

??? ? ??什么是深拷貝?深拷貝和淺拷貝有什么區別

JS語法篇:

????????你真的會使用Vue3的onMounted鉤子函數嗎?Vue3中onMounted的用法詳解

????????對象數據的讀取,看這一篇就夠了!

????????通過array.every()實現數據驗證、權限檢查和一致性檢查,array.some與array.every的區別

????????通過array.some()實現權限檢查、表單驗證、庫存管理、內容審查和數據處理

????????通過array.map()實現數據轉換、創建派生數組、異步數據流處理、搜索和過濾等需求

????????通過array.reduce()實現數據匯總、條件篩選和映射、對象屬性的扁平化、轉換數據格式等

????????通過array.filter()實現數組的數據篩選、數據清洗和鏈式調用

巧妙算法與竅門:

????????多維數組操作,不要再用遍歷循環foreach了,來試試數組展平的小妙招!

????????別再用雙層遍歷循環來做新舊數組對比,尋找新增元素了!

????????shpfile轉GeoJSON且控制轉化精度;如何獲取GeoJSON?GeoJson結構詳解

????????Mapbox添加行政區矢量圖層、分級設色圖層、自定義鼠標懸浮框、添加天地圖底圖等

Element plus拓展:

????????通過el-tree自定義渲染網頁版工作目錄,實現鼠標懸浮顯示完整名稱等

????????el-table實現動態數據的實時排序,一篇文章講清楚elementui的表格排序功能

?????? ?el-table中如何添加漸變色帶、多色色帶

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/38827.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/38827.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/38827.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ctfshow sqli-libs web561--web568

web561 ?id-1 or 1--?id-1 union select 1,2,3--?id-1 union select 1,(select group_concat(column_name) from information_schema.columns where table_nameflags),3-- Your Username is : id,flag4s?id-1 union select 1,(select group_concat(flag4s) from ctfshow.f…

擴展學習|風險評估和風險管理:回顧其基礎上的最新進展

文獻來源&#xff1a;[1]Aven, T. (2016). Risk assessment and risk management: Review of recent advances on their foundation. European journal of operational research, 253(1), 1-13. 文章簡介&#xff1a;大約30-40年前&#xff0c;風險評估和管理被確立為一個科學領…

數據結構 - C/C++ - 鏈表

目錄 結構特性 內存布局 結構樣式 結構拓展 單鏈表 結構定義 節點關聯 插入節點 刪除節點 常見操作 雙鏈表 環鏈表 結構容器 結構設計 結構特性 線性結構的存儲方式 順序存儲 - 數組 鏈式存儲 - 鏈表 線性結構的鏈式存儲是通過任意的存儲單元來存儲線性…

技術分享:分布式數據庫DNS服務器的架構思路

DNS是企業數字化轉型的基石。伴隨微服務或單元化部署的推廣&#xff0c;許多用戶也開始采用分布式數據庫將原來的單體數據庫集群服務架構拆分為大量分布式子服務集群&#xff0c;對應不同的微服務或服務單元。本文將從分布式數據庫DNS服務器的架構需求、架構分析兩方面入手&…

1_插入排序_循環不變式

01_插入排序 #include<stdio.h>void insert_sort(int arr[], int n); void printArray(int arr[], size);int main() {int arr[] {1, 2, 3, 22, 5, 9};int n sizeof(arr) / sizeof(arr[0]);printf("打印原始數組:\n");prinfArray(arr, n);insert_sort(arr, …

湖北大學2024年成人高考函授報名專升本市場營銷專業介紹

在璀璨的學術殿堂中&#xff0c;湖北大學如同一顆璀璨的明珠&#xff0c;熠熠生輝。為了滿足廣大社會人士對于繼續深造、提升自我、實現職業夢想的渴望&#xff0c;湖北大學特別開設了成人高等繼續教育項目&#xff0c;為廣大有志之士敞開了一扇通往知識殿堂的大門。 而今&…

【FFmpeg】av_write_frame函數

目錄 1.av_write_frame1.1 寫入pkt&#xff08;write_packets_common&#xff09;1.1.1 檢查pkt的信息&#xff08;check_packet&#xff09;1.1.2 準備輸入的pkt&#xff08;prepare_input_packet&#xff09;1.1.3 檢查碼流&#xff08;check_bitstream&#xff09;1.1.4 寫入…

【創建者模式-建造者模式】

概要 將一個復雜對象的構建與表示分離&#xff0c;使得同樣的構建過程可以創建不同的表示。 建造者模式包含以下角色 抽象建造者類&#xff08;Builder&#xff09;&#xff1a;這個接口規定要實現復雜對象的那些部分的創建&#xff0c;并不涉及具體的部件對象的創建。具體建…

什么是ISR?

ISR&#xff08;Interrupt Service Routine&#xff0c;中斷服務程序&#xff09;是一個用于處理硬件中斷的特定程序。中斷是硬件或軟件引起的事件&#xff0c;會暫時打斷當前正在運行的任務&#xff0c;以便緊急處理某個事件。ISR的目的是快速響應中斷信號&#xff0c;執行所需…

在WSL Ubuntu中啟用root用戶的SSH服務

在 Ubuntu 中&#xff0c;默認情況下 root 用戶是禁用 SSH 登錄的&#xff0c;這是為了增加系統安全性。 一、修改配置 找到 PermitRootLogin 行&#xff1a;在文件中找到 PermitRootLogin 配置項。默認情況下&#xff0c;它通常被設置為 PermitRootLogin prohibit-password 或…

一篇文章學會【node.js安裝以及Vue-Cli腳手架搭建】

一.為什么搭建Vue-Cli (1).傳統的前端項目結構&#xff1a; 一個項目中有許多html文件&#xff0c;每一個html文件都是相互獨立的&#xff0c; 如果需要在頁面中導入一些外部依賴的組件&#xff0c;就需要在每一個html文件中都需要導入&#xff0c;非常麻煩 (2).現在的前端…

A股低開高走,近3000點,行情要啟動了嗎?

A股低開高走&#xff0c;近3000點&#xff0c;行情要啟動了嗎&#xff1f; 今天的A股&#xff0c;讓人瞪目結舌了&#xff0c;你們知道是為什么嗎&#xff1f;盤面上出現2個重要信號&#xff0c;一起來看看&#xff1a; 1、今天兩市低開高走&#xff0c;銀行板塊護盤指數&…

Windows 下后臺啟動java項目的 jar 包

java -jar swagger.jar 的dos窗口 后臺啟動 jar 包&#xff1a; 使用 javaw.exe 啟動 jar 包&#xff0c;并不會在窗口打印日志&#xff0c;而且會直接在后臺運行進程&#xff0c;關掉窗口&#xff0c;進程繼續跑 javaw -jar swagger.jar 關閉進程&#xff1a; 后臺啟動的 …

大數據面試題之Spark(7)

Spark實現wordcount Spark Streaming怎么實現數據持久化保存? Spark SQL讀取文件&#xff0c;內存不夠使用&#xff0c;如何處理? Spark的lazy體現在哪里? Spark中的并行度等于什么 Spark運行時并行度的設署 Spark SQL的數據傾斜 Spark的exactly-once Spark的RDD和p…

大話C語言:第26篇 靜態庫

1 靜態庫概述 C語言靜態庫&#xff08;Static Library&#xff09;是一種包含一組目標文件的歸檔文件&#xff0c;這些目標文件通常是由多個C語言源文件編譯而成的。靜態庫在程序編譯時被鏈接到目標程序中&#xff0c;成為程序的一部分&#xff0c;因此在運行時不再需要額外的…

java Lambda表達式介紹

Lambda 表達式是 Java 8 中引入的一種語法糖,用于簡化使用函數式接口的代碼編寫。它使得 Java 編程更加簡潔和靈活,特別是在處理集合數據、事件監聽器等方面提供了便利。 Lambda 表達式的語法 Lambda 表達式的基本語法如下: (parameters) -> expression或者是一個代碼…

盤古5.0,靠什么去解最難的題?

文&#xff5c;周效敬 編&#xff5c;王一粟 當大模型的競爭開始拼落地&#xff0c;商業化在B端和C端都展開了自由生長。 在B端&#xff0c;借助云計算向千行萬業扎根&#xff1b;在C端&#xff0c;通過軟件App和智能終端快速迭代。 在華為&#xff0c;這家曾經以通信行業起…

Error: A JNl error has occurred, please check your installation and try again.

Eclipse 運行main方法的時候報錯&#xff1a;Error: A JNl error has occurred, please check your installation and try again. 一、問題分析 導致這個問題&#xff0c;主要原因&#xff0c;我認為是在新版本中&#xff0c;默認的JDK編譯版本與我們配置的JDK版本不一致導致的…

公網環境使用Potplayer遠程訪問家中群暉NAS搭建的WebDAV聽歌看電影

文章目錄 前言1 使用環境要求&#xff1a;2 配置webdav3 測試局域網使用potplayer訪問webdav4 內網穿透&#xff0c;映射至公網5 使用固定地址在potplayer訪問webdav 前言 本文主要介紹如何在Windows設備使用potplayer播放器遠程訪問本地局域網的群暉NAS中的影視資源&#xff…

告別流失,擁抱增長!Xinstall智能邀請系統,讓你的App拉新更高效

在移動互聯網時代&#xff0c;App的推廣和運營面臨著諸多挑戰。其中&#xff0c;如何有效地進行邀請拉新活動&#xff0c;吸引更多新用戶&#xff0c;成為了每個運營者都需要面對的問題。今天&#xff0c;我們將為大家介紹一款能夠幫助你輕松解決這一難題的神器——Xinstall。 …