摘要:瀏覽器是多進(jìn)程的詳情看我上篇總結(jié)瀏覽器執(zhí)行機(jī)制的文章深入前端徹底搞懂瀏覽器運(yùn)行機(jī)制瀏覽器每打開一個標(biāo)簽頁,就相當(dāng)于創(chuàng)建了一個獨立的瀏覽器進(jìn)程。執(zhí)行異步操作事件完成,回調(diào)函數(shù)進(jìn)入。主線程從讀取回調(diào)函數(shù)并執(zhí)行。
最近看了很多關(guān)于JS運(yùn)行機(jī)制的文章,每篇都獲益匪淺,但各有不同,所以在這里對這幾篇文章里說的很精辟的地方做一個總結(jié),參考文章鏈接見最后。本文博客地址了解進(jìn)程和線程
進(jìn)程是應(yīng)用程序的執(zhí)行實例,每一個進(jìn)程都是由私有的虛擬地址空間、代碼、數(shù)據(jù)和其它系統(tǒng)資源所組成;進(jìn)程在運(yùn)行過程中能夠申請創(chuàng)建和使用系統(tǒng)資源(如- 獨立的內(nèi)存區(qū)域等),這些資源也會隨著進(jìn)程的終止而被銷毀。
而線程則是進(jìn)程內(nèi)的一個獨立執(zhí)行單元,在不同的線程之間是可以共享進(jìn)程資源的,所以在多線程的情況下,需要特別注意對臨界資源的訪問控制。
在系統(tǒng)創(chuàng)建進(jìn)程之后就開始啟動執(zhí)行進(jìn)程的主線程,而進(jìn)程的生命周期和這個主線程的生命周期一致,主線程的退出也就意味著進(jìn)程的終止和銷毀。
主線程是由系統(tǒng)進(jìn)程所創(chuàng)建的,同時用戶也可以自主創(chuàng)建其它線程,這一系列的線程都會并發(fā)地運(yùn)行于同一個進(jìn)程中。
瀏覽器是多進(jìn)程的詳情看我上篇總結(jié)瀏覽器執(zhí)行機(jī)制的文章-深入前端-徹底搞懂瀏覽器運(yùn)行機(jī)制
瀏覽器每打開一個標(biāo)簽頁,就相當(dāng)于創(chuàng)建了一個獨立的瀏覽器進(jìn)程。
Browser進(jìn)程:瀏覽器的主進(jìn)程(負(fù)責(zé)協(xié)調(diào)、主控),只有一個。作用有
第三方插件進(jìn)程:每種類型的插件對應(yīng)一個進(jìn)程,僅當(dāng)使用該插件時才創(chuàng)建
GPU進(jìn)程:最多一個,用于3D繪制等
瀏覽器渲染進(jìn)程(瀏覽器內(nèi)核)
javascript是一門單線程語言jS運(yùn)行在瀏覽器中,是單線程的,但每個tab標(biāo)簽頁都是一個進(jìn)程,都含有不同JS線程分別執(zhí)行,,一個Tab頁(renderer進(jìn)程)中無論什么時候都只有一個JS線程在運(yùn)行JS程序
既然是單線程的,在某個特定的時刻只有特定的代碼能夠被執(zhí)行,并阻塞其它的代碼。而瀏覽器是事件驅(qū)動的(Event driven),瀏覽器中很多行為是異步(Asynchronized)的,會創(chuàng)建事件并放入執(zhí)行隊列中。javascript引擎是單線程處理它的任務(wù)隊列,你可以理解成就是普通函數(shù)和回調(diào)函數(shù)構(gòu)成的隊列。當(dāng)異步事件發(fā)生時,如(鼠標(biāo)點擊事件發(fā)生、定時器觸發(fā)事件發(fā)生、XMLHttpRequest完成回調(diào)觸發(fā)等),將他們放入執(zhí)行隊列,等待當(dāng)前代碼執(zhí)行完成。
javascript引擎是基于事件驅(qū)動單線程執(zhí)行的,JS引擎一直等待著任務(wù)隊列中任務(wù)的到來,然后加以處理,瀏覽器無論什么時候都只有一個JS線程在運(yùn)行JS程序。所以一切javascript版的"多線程"都是用單線程模擬出來的
為什么JavaScript是單線程?與它的用途有關(guān)。作為瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操作DOM。這決定了它只能是單線程,否則會帶來很復(fù)雜的同步問題。比如,假定JavaScript同時有兩個線程,一個線程在某個DOM節(jié)點上添加內(nèi)容,另一個線程刪除了這個節(jié)點,這時瀏覽器應(yīng)該以哪個線程為準(zhǔn)?
任務(wù)隊列"任務(wù)隊列"是一個事件的隊列(也可以理解成消息的隊列),IO設(shè)備完成一項任務(wù),就在"任務(wù)隊列"中添加一個事件,表示相關(guān)的異步任務(wù)可以進(jìn)入"執(zhí)行棧"了。主線程讀取"任務(wù)隊列",就是讀取里面有哪些事件。
"任務(wù)隊列"中的事件,除了IO設(shè)備的事件以外,還包括一些用戶產(chǎn)生的事件(比如鼠標(biāo)點擊、頁面滾動等等),ajax請求等。只要指定過回調(diào)函數(shù),這些事件發(fā)生時就會進(jìn)入"任務(wù)隊列",等待主線程讀取。
所謂"回調(diào)函數(shù)"(callback),就是那些會被主線程掛起來的代碼。異步任務(wù)必須指定回調(diào)函數(shù),當(dāng)主線程開始執(zhí)行異步任務(wù),就是執(zhí)行對應(yīng)的回調(diào)函數(shù)。
"任務(wù)隊列"是一個先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),排在前面的事件,優(yōu)先被主線程讀取。主線程的讀取過程基本上是自動的,只要執(zhí)行棧一清空,"任務(wù)隊列"上第一位的事件就自動進(jìn)入主線程。但是,由于存在后文提到的"定時器"功能,主線程首先要檢查一下執(zhí)行時間,某些事件只有到了規(guī)定的時間,才能返回主線程。
同步和異步任務(wù)既然js是單線程,那么問題來了,某一些非常耗時間的任務(wù)就會導(dǎo)致阻塞,難道必須等前面的任務(wù)一步一步執(zhí)行玩嗎?
比如我再排隊就餐,前面很長的隊列,我一直在那里等豈不是很傻逼,說以就會有排號系統(tǒng)產(chǎn)生,我們訂餐后給我們一個號碼,叫到號碼直接去就行了,沒交我們之前我們可以去干其他的事情。
因此聰明的程序員將任務(wù)分為兩類:
同步任務(wù):同步任務(wù)指的是,在主線程上排隊執(zhí)行的任務(wù),只有前一個任務(wù)執(zhí)行完畢,才能執(zhí)行后一個任務(wù);
異步任務(wù):異步任務(wù)指的是,不進(jìn)入主線程、而進(jìn)入"任務(wù)隊列"(Event queue)的任務(wù),只有"任務(wù)隊列"通知主線程,某個異步任務(wù)可以執(zhí)行了,該任務(wù)才會進(jìn)入主線程執(zhí)行。
任務(wù)有更精細(xì)的定義:macro-task(宏任務(wù)):包括整體代碼script(同步宏任務(wù)),setTimeout、setInterval(異步宏任務(wù))
micro-task(微任務(wù)):Promise,process.nextTick,ajax請求(異步微任務(wù))
macrotask(又稱之為宏任務(wù))可以理解是每次執(zhí)行棧執(zhí)行的代碼就是一個宏任務(wù)(包括每次從事件隊列中獲取一個事件回調(diào)并放到執(zhí)行棧中執(zhí)行)
每一個task會從頭到尾將這個任務(wù)執(zhí)行完畢,不會執(zhí)行其它
瀏覽器為了能夠使得JS內(nèi)部task與DOM任務(wù)能夠有序的執(zhí)行,會在一個task執(zhí)行結(jié)束后,在下一個 task 執(zhí)行開始前,對頁面進(jìn)行重新渲染
(task->渲染->task->...)
也就是說,在當(dāng)前task任務(wù)后,下一個task之前,在渲染之前
所以它的響應(yīng)速度相比setTimeout(setTimeout是task)會更快,因為無需等渲染
也就是說,在某一個macrotask執(zhí)行完后,就會將在它執(zhí)行期間產(chǎn)生的所有microtask都執(zhí)行完畢(在渲染前)
主線程運(yùn)行的時候,產(chǎn)生堆(heap)和棧(stack),棧中的代碼調(diào)用各種外部API,它們在"任務(wù)隊列"中加入各種事件(click,load,done)。只要棧中的代碼執(zhí)行完畢,主線程就會去讀取"任務(wù)隊列",依次執(zhí)行那些事件所對應(yīng)的回調(diào)函數(shù)。
那怎么知道主線程執(zhí)行棧為執(zhí)行完畢?js引擎存在monitoring process進(jìn)程,會持續(xù)不斷的檢查主線程執(zhí)行棧是否為空,一旦為空,就會去Event Queue那里檢查是否有等待被調(diào)用的函數(shù)。
第一輪事件循環(huán):
主線程執(zhí)行js整段代碼(宏任務(wù)),將ajax、setTimeout、promise等回調(diào)函數(shù)注冊到Event Queue,并區(qū)分宏任務(wù)和微任務(wù)。
主線程提取并執(zhí)行Event Queue 中的ajax、promise等所有微任務(wù),并注冊微任務(wù)中的異步任務(wù)到Event Queue。
第二輪事件循環(huán):
主線程提取Event Queue 中的第一個宏任務(wù)(通常是setTimeout)。
主線程執(zhí)行setTimeout宏任務(wù),并注冊setTimeout代碼中的異步任務(wù)到Event Queue(如果有)。
執(zhí)行Event Queue中的所有微任務(wù),并注冊微任務(wù)中的異步任務(wù)到Event Queue(如果有)。
類似的循環(huán):宏任務(wù)每執(zhí)行完一個,就清空一次事件隊列中的微任務(wù)。
注意:事件隊列中分“宏任務(wù)隊列”和“微任務(wù)隊列”,每執(zhí)行一次任務(wù)都可能注冊新的宏任務(wù)或微任務(wù)到相應(yīng)的任務(wù)隊列中,只要遵循“每執(zhí)行一個宏任務(wù),就會清空一次事件隊列中的所有微任務(wù)”這一循環(huán)規(guī)則,就不會弄亂。
說了那么多來點實例吧 ajax普通異步請求實例let data = []; $.ajax({ url:www.javascript.com, data:data, success:() => { console.log("發(fā)送成功!"); } }) console.log("代碼執(zhí)行結(jié)束");
1.執(zhí)行整個代碼,遇到ajax異步操作
2.ajax進(jìn)入Event Table,注冊回調(diào)函數(shù)success。
3.執(zhí)行console.log("代碼執(zhí)行結(jié)束")。
4.執(zhí)行ajax異步操作
5.ajax事件完成,回調(diào)函數(shù)success進(jìn)入Event Queue。
5.主線程從Event Queue讀取回調(diào)函數(shù)success并執(zhí)行。
setTimeout(function(){ console.log("定時器開始啦") }); new Promise(function(resolve){ console.log("馬上執(zhí)行for循環(huán)啦"); for(var i = 0; i < 10000; i++){ i == 99 && resolve(); } }).then(function(){ console.log("執(zhí)行then函數(shù)啦") }); console.log("代碼執(zhí)行結(jié)束");
1.整段代碼作為宏任務(wù)執(zhí)行,遇到setTimeout宏任務(wù)分配到宏任務(wù)Event Queue中
2.遇到promise內(nèi)部為同步方法直接執(zhí)行-“馬上執(zhí)行for循環(huán)啦”
3.注冊then回調(diào)到Eventqueen
4.主代碼宏任務(wù)執(zhí)行完畢-“代碼執(zhí)行結(jié)束”
5.主代碼宏任務(wù)結(jié)束被monitoring process進(jìn)程監(jiān)聽到,主任務(wù)執(zhí)行Event Queue的微任務(wù)
6.微任務(wù)執(zhí)行完畢-“執(zhí)行then函數(shù)啦”
7.執(zhí)行宏任務(wù)console.log("定時器開始啦")
console.log("1"); // 1 6 7 2 4 5 9 10 11 8 3 // 記作 set1 setTimeout(function () { console.log("2"); // set4 setTimeout(function() { console.log("3"); }); // pro2 new Promise(function (resolve) { console.log("4"); resolve(); }).then(function () { console.log("5") }) }) // 記作 pro1 new Promise(function (resolve) { console.log("6"); resolve(); }).then(function () { console.log("7"); // set3 setTimeout(function() { console.log("8"); }); }) // 記作 set2 setTimeout(function () { console.log("9"); // 記作 pro3 new Promise(function (resolve) { console.log("10"); resolve(); }).then(function () { console.log("11"); }) })第一輪事件循環(huán)
1.整體script作為第一個宏任務(wù)進(jìn)入主線程,遇到console.log,輸出1。
2.遇到set1,其回調(diào)函數(shù)被分發(fā)到宏任務(wù)Event Queue中。
3.遇到pro1,new Promise直接執(zhí)行,輸出6。then被分發(fā)到微任務(wù)Event Queue中。
4.遇到了set2,其回調(diào)函數(shù)被分發(fā)到宏任務(wù)Event Queue中。
主線程的整段js代碼(宏任務(wù))執(zhí)行完,開始清空所有微任務(wù);主線程執(zhí)行微任務(wù)pro1,輸出7;遇到set3,注冊回調(diào)函數(shù)。
第二輪事件循環(huán)1.主線程執(zhí)行隊列中第一個宏任務(wù)set1,輸出2;代碼中遇到了set4,注冊回調(diào);又遇到了pro2,new promise()直接執(zhí)行輸出4,并注冊回調(diào);
2.set1宏任務(wù)執(zhí)行完畢,開始清空微任務(wù),主線程執(zhí)行微任務(wù)pro2,輸出5。
第三輪事件循環(huán)1.主線程執(zhí)行隊列中第一個宏任務(wù)set2,輸出9;代碼中遇到了pro3,new promise()直接輸出10,并注冊回調(diào);
2.set2宏任務(wù)執(zhí)行完畢,開始情況微任務(wù),主線程執(zhí)行微任務(wù)pro3,輸出11。
類似循環(huán)...
所以最后輸出結(jié)果為1、6、7、2、4、5、9、10、11、8、3。
參考文章https://www.cnblogs.com/Mainz...
http://www.ruanyifeng.com/blo...
https://juejin.im/post/5b4dfb...
https://juejin.im/post/5b879a...
https://juejin.im/post/59e85e...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/105674.html
摘要:瀏覽器是多進(jìn)程的詳情看我上篇總結(jié)瀏覽器執(zhí)行機(jī)制的文章深入前端徹底搞懂瀏覽器運(yùn)行機(jī)制瀏覽器每打開一個標(biāo)簽頁,就相當(dāng)于創(chuàng)建了一個獨立的瀏覽器進(jìn)程。執(zhí)行異步操作事件完成,回調(diào)函數(shù)進(jìn)入。主線程從讀取回調(diào)函數(shù)并執(zhí)行。 最近看了很多關(guān)于JS運(yùn)行機(jī)制的文章,每篇都獲益匪淺,但各有不同,所以在這里對這幾篇文章里說的很精辟的地方做一個總結(jié),參考文章鏈接見最后。本文博客地址 了解進(jìn)程和線程 進(jìn)程是應(yīng)用...
摘要:當(dāng)這些異步任務(wù)發(fā)生的時候,它們將會被放入瀏覽器的事件任務(wù)隊列中去,等到運(yùn)行時執(zhí)行線程空閑時候才會按照隊列先進(jìn)先出的原則被一一執(zhí)行,但終究還是單線程。 瀏覽器是多進(jìn)程的 showImg(https://segmentfault.com/img/remote/1460000019706956?w=815&h=517); Browser進(jìn)程: 瀏覽器的主進(jìn)程(負(fù)責(zé)協(xié)調(diào)、主控),只有一個。 負(fù)...
摘要:當(dāng)這些異步任務(wù)發(fā)生的時候,它們將會被放入瀏覽器的事件任務(wù)隊列中去,等到運(yùn)行時執(zhí)行線程空閑時候才會按照隊列先進(jìn)先出的原則被一一執(zhí)行,但終究還是單線程。 瀏覽器是多進(jìn)程的 showImg(https://segmentfault.com/img/remote/1460000019706956?w=815&h=517); Browser進(jìn)程: 瀏覽器的主進(jìn)程(負(fù)責(zé)協(xié)調(diào)、主控),只有一個。 負(fù)...
摘要:檢查宏任務(wù)隊列,發(fā)現(xiàn)有的回調(diào)函數(shù)立即執(zhí)行回調(diào)函數(shù)輸出。接著遇到它的作用是在后將回調(diào)函數(shù)放到宏任務(wù)隊列中這個任務(wù)在再下一次的事件循環(huán)中執(zhí)行。 為什么會寫這篇博文呢? 前段時間,和頭條的小伙伴聊天問頭條面試前端會問哪些問題,他稱如果是他面試的話,event-loop肯定是要問的。那天聊了蠻多,event-loop算是給我留下了很深的印象,原因很簡單,因為之前我從未深入了解過,如果是面試的時...
摘要:圖數(shù)據(jù)類型圖引用類型深淺拷貝問題不知道什么是深拷貝和淺拷貝的請先去并在調(diào)試臺自己操作一下,這篇文章只會說明為何中會有這種問題。所以有的時候我們?yōu)榱吮苊鉁\拷貝,會用一些方式實現(xiàn)深拷貝。 首先要了解的js基礎(chǔ) 基本數(shù)據(jù)類型:Object、undefined、null、Boolean、Number、String、Symbol (ES6新加) Object包括: Array 、Date 、R...
閱讀 813·2021-11-18 10:02
閱讀 2503·2021-11-11 16:54
閱讀 2750·2021-09-02 09:45
閱讀 654·2019-08-30 12:52
閱讀 2774·2019-08-29 14:04
閱讀 2745·2019-08-29 12:39
閱讀 448·2019-08-29 12:27
閱讀 1887·2019-08-26 13:23