摘要:事件觸發線程主要負責將準備好的事件交給引擎線程執行。進程瀏覽器渲染進程瀏覽器內核,主要負責頁面的渲染執行以及事件的循環。第二輪循環結束。
將自己讀到的比較好的文章分享出來,大家互相學習,各位大佬有好的文章也可以留個鏈接互相學習,萬分感謝!線程與進程
關于線程與進程的關系可以用下面的圖進行說明:
進程好比圖中的工廠,有多帶帶的專屬自己的工廠資源。
線程好比圖中的工人,多個工人在一個工廠中協作工作,工廠與工人是 1:n的關系。
多個工廠之間獨立存在。
而官方的說法是:
進程是 CPU資源分配的最小單位。
線程是 CPU調度的最小單位。
從更直觀的例子來看,可以打開任務管理器查看,第一個 tab便是進程列表,每一個進程占有的 CPU資源和內存資源的比例很直觀的展示出來。
為什么js是單線程初學計算機語言的時候,無論是 C、C++還是 JAVA,都是支持多線程,偏偏 JavaScript是單線程,不支持多線程,這也跟 JavaScript的作用有關,都知道 JavaScript是主要運行在瀏覽器的腳本語言,最終操作的是頁面的 DOM結構,當兩個 JavaScript腳本同時修改頁面的同一個 DOM節點時,瀏覽器該執行哪個呢?所以當時設計 JavaScript時,便要求當前修改操作完成后方可進行下一步修改操作。
瀏覽器是支持多進程同樣我們打開瀏覽器的任務管理器,以下圖為例:
瀏覽器的每一個 tab頁都是一個進程,有對應的內存占用空間、 CPU使用量以及進程ID。 新打開一個 tab頁時,都會新建一個進程,所以就有一個 tab頁對應一個進程的說法,但是這種說法又是錯誤的,因為瀏覽器有自己的優化機制,當我們打開多個空白的 tab頁時,瀏覽器會將這多個空白頁的進程合并為一個,從而減少了進程的數量個數。
瀏覽器內核瀏覽器內核中有多個進程在同步工作,今天涉及到的瀏覽器的進程主要包括以下進程:
Browser 進程
主進程,主要負責頁面管理以及管理其他進程的創建和銷毀等,常駐的線程有:
GUI渲染線程
JS引擎線程
事件觸發線程
定時器觸發線程
HTTP請求線程
GUI渲染線程
主要負責頁面的渲染,解析HTML、CSS,構建DOM樹,布局和繪制等。
當界面需要重繪或者由于某種操作引發回流時,將執行該線程。
該線程與JS引擎線程互斥,當執行JS引擎線程時,GUI渲染會被掛起,當任務隊列空閑時,JS引擎才會去執行GUI渲染。
JS引擎線程
該線程當然是主要負責處理 JavaScript腳本,執行代碼。
也是主要負責執行準備好待執行的事件,即定時器計數結束,或者異步請求成功并正確返回時,將依次進入任務隊列,等待 JS引擎線程的執行。
當然,該線程與 GUI渲染線程互斥,當 JS引擎線程執行 JavaScript腳本時間過長,將導致頁面渲染的阻塞。
事件觸發線程
主要負責將準備好的事件交給 JS引擎線程執行。
比如 setTimeout定時器計數結束, ajax等異步請求成功并觸發回調函數,或者用戶觸發點擊事件時,該線程會將整裝待發的事件依次加入到任務隊列的隊尾,等待 JS引擎線程的執行。
定時器觸發線程
顧名思義,負責執行異步定時器一類的函數的線程,如: setTimeout,setInterval。
主線程依次執行代碼時,遇到定時器,會將定時器交給該線程處理,當計數完畢后,事件觸發線程會將計數完畢后的事件加入到任務隊列的尾部,等待JS引擎線程執行。
HTTP請求線程
顧名思義,負責執行異步請求一類的函數的線程,如: Promise,anxios,ajax等。
主線程依次執行代碼時,遇到異步請求,會將函數交給該線程處理,當監聽到狀態碼變更,如果有回調函數,事件觸發線程會將回調函數加入到任務隊列的尾部,等待JS引擎線程執行。
多個線程之間配合工作,各司其職。
Render 進程
瀏覽器渲染進程(瀏覽器內核),主要負責頁面的渲染、JS執行以及事件的循環。同步任務和異步任務
同步任務 即可以立即執行的任務,例如 console.log() 打印一條日志、聲明一個變量或者執行一次加法操作等。
異步任務 相反不會立即執行的事件任務。異步任務包括宏任務和微任務(后面會進行解釋~)。
常見的異步操作:
Ajax
DOM的事件操作
setTimeout
Promise的then方法
Node的讀取文件
下圖給出了同步任務與異步任務的執行流程:
棧 就像是一個容器,任務都是在棧中執行。
主線程 就像是操作員,負責執行棧中的任務。
任務隊列 就像是等待被加工的物品。
異步任務完成注冊后會將回調函數加入任務隊列等待主線程執行。
執行棧中的同步任務執行完畢后,會查看并讀取任務隊列中的事件函數,于是任務隊列的函數結束等待狀態,進入執行棧,開始執行。
那么任務到底是如何入棧和出棧的呢?可以用一小段代碼進行解釋。
入棧與出棧以下面的代碼為例:
console.log(1); function fn1(){ console.log(2); } function fn2(){ console.log(3); fn1(); } setTimeout(function(){ console.log(4); }, 2000); fn2(); console.log(5);
所以上面代碼運行的結果為:1,3,2,5,4。
宏任務和微任務異步任務分為宏任務和微任務,宏任務隊列可以有多個,微任務隊列只有一個。
宏任務和微任務的執行方式在瀏覽器和 Node 中有差異。
宏任務(macrotask)script(全局任務), setTimeout, setInterval, setImmediate, I/O, UI rendering微任務(macrotask)
process.nextTick, Promise.then(), Object.observe, MutationObserver
在微任務中 process.nextTick 優先級高于Promise
當一個異步任務入棧時,主線程判斷該任務為異步任務,并把該任務交給異步處理模塊處理,當異步處理模塊處理完打到觸發條件時,根據任務的類型,將回調函數壓入任務隊列。
如果是宏任務,則新增一個宏任務隊列,任務隊列中的宏任務可以有多個來源。
如果是微任務,則直接壓入微任務隊列。
所以上圖的任務隊列可以繼續細化一下:
那么當棧為空時,宏任務和微任務的執行機制又是什么呢?
Event Loop到這里,除了上面的問題,我們已經把事件循環的最基本的處理方式搞清楚了,但具體到異步任務中的宏任務和微任務,還沒有弄明白。我們可以先順一遍執行機制:
從全局任務 script開始,任務依次進入棧中,被主線程執行,執行完后出棧。
遇到異步任務,交給異步處理模塊處理,對應的異步處理線程處理異步任務需要的操作,例如定時器的計數和異步請求監聽狀態的變更。
當異步任務達到可執行狀態時,事件觸發線程將回調函數加入任務隊列,等待棧為空時,依次進入棧中執行。
到這問題就來了,當異步任務進入棧執行時,是宏任務還是微任務呢?
由于執行代碼入口都是全局任務 script,而全局任務屬于宏任務,所以當棧為空,同步任務任務執行完畢時,會先執行微任務隊列里的任務。
微任務隊列里的任務全部執行完畢后,會讀取宏任務隊列中拍最前的任務。
執行宏任務的過程中,遇到微任務,依次加入微任務隊列。
棧空后,再次讀取微任務隊列里的任務,依次類推。
實例解析回到最開始的那段代碼,現在我們可以一步一步的看一下執行順序。
console.log(1); setTimeout(function(){ console.log(2); }, 0); setTimeout(function(){ console.log(3) },2000) console.log(4);
從全局任務入口,首先打印日志 1,
遇到宏任務 setTimeout,交給異步處理模塊,我們暫且先記為 setTimeout1,
再次遇到宏任務 setTimeout,交給異步處理模塊,我們暫且先記為 setTimeout2,
順序執行,打印日志 4,
此時同步任務已執行完畢,讀取宏任務隊列的任務,先執行 setTimeout1的回調函數,因為定時器的等待時間為 0秒,所以會直接輸出 2,但是 W3C在 HTML標準中規定,規定要求 setTimeout中低于 4ms的時間間隔算為 4ms,
由于瀏覽器在執行以上三步時,并未耗時很久,所以當宏任務 setTimeout1執行完時, setTimeout2的等待時間并未結束,所以在 2秒后打印日志 3,實際上并未等待2秒。
下面我們可以再看一個實例:
setTimeout(function(){ console.log(1); Promise.resolve().then(function(){ console.log(2) }) },0) setTimeout(function(){ console.log(3) },0) Promise.resolve().then(function(){ console.log(4) }); console.log(5)
當代碼中遇到了異步請求的事件,又該如何執行,根據上面總結的執行機制,又該得到什么樣的結果?
第一輪循環
同樣從全局任務入口,遇到宏任務 setTimeout,交給異步處理模塊,我們暫且先記為 setTimeout1,由于等待時間為 0,直接加入宏任務隊列。
再次遇到宏任務 setTimeout,交給異步處理模塊,我們暫且先記為 setTimeout2,同樣直接加入宏任務隊列。
遇到微任務 then(),加入微任務隊列。
最后遇到打印語句,直接打印日志 5。
第一輪循環結束后,可以畫出下圖:
第二輪循環
棧空后,先執行微任務隊列中的 then()方法,輸出 4,此時微任務隊列為空。
讀取宏任務隊列的最靠前的任務 setTimeout1。
先直接執行打印語句,打印日志 1,又遇到微任務 then(),加入微任務隊列。第二輪循環結束。
第三輪循環
先執行微任務隊列中的 then()方法,輸出 2,此時微任務隊列為空。
繼續讀取宏任務隊列的最靠前的任務 setTimeout2。
直接執行打印語句,打印日志 3。第三輪循環結束,執行完畢。
最后我們是我們的boss,歡迎大家在評論區留言寫出自己心中的那個正確答案。
console.log(1); setTimeout(function(){ console.log(2); new Promise(function(resolve, reject){ console.log(3); resolve(); }).then(function(){ console.log(4); }) }) new Promise(function(resolve, reject){ console.log(5); resolve(); }).then(function(){ console.log(6); }) setTimeout(function(){ console.log(7) }) setTimeout(function(){ console.log(8); new Promise(function(resolve, reject){ console.log(9); resolve(); }).then(function(){ console.log(10); }) }) new Promise(function(resolve){ console.log(11); resolve(); }).then(function(){ console.log(12) }) console.log(13)
github地址:https://github.com/ABCDdouyaer/a_article_per_day/tree/master/0001
原文鏈接:https://mp.weixin.qq.com/s/9_hZX_xWSr3Gd1X_2_WOsA
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/108788.html
摘要:主線程不斷重復上面的三步,此過程也就是常說的事件循環。所以主線程代碼執行時間過長,會阻塞事件循環的執行。參考資料這一次,徹底弄懂執行機制任務隊列的順序機制事件循環搞懂異步事件輪詢與中的事件循環 1. 說明 讀過本文章后,您能知道: JavaScript代碼在瀏覽器中的執行機制和事件循環 面試中經常遇到的代碼輸出順序問題 首先通過一段代碼來驗證你是否了解代碼輸出順序,如果你不知道輸出...
摘要:了解事件循環機制有助于理解的執行過程,同時這也是面試常見題。那么這個回調函數將在何時由誰執行呢已知是瀏覽器環境提供的,因此瀏覽器將對它進行處理,瀏覽器會在本次事件完成,即計時結束后,將回調函數加入循環隊列中,然后等待被加入執行棧執行。 如果有人問JavaScript是什么,也許你會說它是一個單線程、非阻塞、異步、解釋型的腳本語言。那么作為一個單線程語言,它是怎么實現非阻塞、異步的?這就...
摘要:如果沒有其他異步任務要處理比如到期的定時器,會一直停留在這個階段,等待請求返回結果。執行的執行事件關閉請求的,例如事件循環的每一次循環都需要依次經過上述的階段。因此,才會早于執行。 showImg(https://segmentfault.com/img/bVbnY76); 概念 同步任務(Synchronous) 在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行后一個任務 ...
摘要:主線程要明確的一點是,主線程跟執行棧是不同概念,主線程規定現在執行執行棧中的哪個事件。主線程循環即主線程會不停的從執行棧中讀取事件,會執行完所有棧中的同步代碼。以上參考資料詳解中的事件循環機制中的事件循環運行機制詳解再談 showImg(https://segmentfault.com/img/remote/1460000015317437?w=1920&h=1080); 前言 大家都...
摘要:前端基礎進階正是圍繞這條線索慢慢展開,而事件循環機制,則是這條線索的最關鍵的知識點。特別是中正式加入了對象之后,對于新標準中事件循環機制的理解就變得更加重要。之后全局上下文進入函數調用棧。 showImg(https://segmentfault.com/img/remote/1460000008811705); JavaScript的學習零散而龐雜,因此很多時候我們學到了一些東西,但...
摘要:事件循環機制首先區分進程和線程進程是資源分配的最小單位系統會給它分配內存不同的進程之間是可以同學的,如管道命名管道消息隊列一個進程里有單個或多個線程瀏覽器是多進程的,因為系統給它的進程分配了資源內存打開會有一個主進程,每打開一個頁就有一個獨 JS JavaScript事件循環機制 首先區分進程和線程 進程是cpu資源分配的最小單位(系統會給它分配內存) 不同的進程之間是可以同學的,如...
閱讀 660·2021-11-15 11:39
閱讀 2895·2021-10-08 10:04
閱讀 3259·2019-08-30 10:57
閱讀 3021·2019-08-26 13:25
閱讀 1902·2019-08-26 12:14
閱讀 2633·2019-08-23 15:27
閱讀 2992·2019-08-23 15:18
閱讀 1773·2019-08-23 14:26