摘要:引言學(xué)習(xí)的時候,經(jīng)常聽人說,即是異步的,又是單線程的。所以我們說是異步單線程的。參考從瀏覽器多進程到單線程,運行機制最全面的一次梳理運行機制詳解再談異步機制詳解運行原理解析并發(fā)模型與事件循環(huán)
引言
學(xué)習(xí)javascipt的時候,經(jīng)常聽人說,javascipt即是異步的,又是單線程的。究竟什么是異步,什么是單線程?javascript在瀏覽器中的運行機制是怎么樣的?什么是eventloop,task queue?怎么寫異步函數(shù)?相信讀完這篇文章,相信你會對上面問題有一個全面的認識。
全面了解瀏覽器 瀏覽器有許多進程:Browser進程:瀏覽器的主進程(負責協(xié)調(diào)、主控),只有一個。
第三方插件進程:每種類型的插件對應(yīng)一個進程,僅當使用該插件時才創(chuàng)建
GPU進程:最多一個,用于3D繪制等
瀏覽器渲染進程(瀏覽器內(nèi)核)(Renderer進程,內(nèi)部是多線程的)
在瀏覽器渲染進程中有許多線程:渲染引擎線程:顧名思義,該線程負責頁面的渲染
JS引擎線程:負責JS的解析和執(zhí)行(主線程)
定時觸發(fā)器線程:處理定時事件,比如setTimeout, setInterval
事件觸發(fā)線程:處理DOM事件
異步http請求線程:處理http請求
雖然JavaScript是單線程的(說的是JS引擎線程),可是瀏覽器內(nèi)部不是單線程的。一些I/O操作、定時器的計時和事件監(jiān)聽(click, keydown...)等都是由瀏覽器提供的其他線程來完成的。
主線程和渲染引擎線程互斥,因為渲染的時候主線程可能通過dom操作渲染結(jié)果,所以主線程必須被阻塞
之前傻傻的分不清楚單線程多線程,同步異步。其實很簡單
異步的判斷標準:是否阻塞,同步阻塞,異步不阻塞。
單線程的判斷標準:一次是否只做一件事。
JS引擎一次只做一件事。遇到異步任務(wù)并不會阻塞后面的同步任務(wù)(不等待)。所以我們說JS是異步 單線程的。需要注意的是JS引擎其實并不提供異步的支持,異步支持主要依賴于運行環(huán)境(瀏覽器或Node.js)。
while阻塞實驗var start = new Date(); while(new Date() - start < 100000) { // delay 10 sec ; }
上面代碼在chrome控制臺輸入可以手動阻塞當前頁面的js主線程10s。然后我們在當前頁面輸入console.log(1),當前頁面無反應(yīng),在另外的頁面輸入console.log(1)直接打印
說明瀏覽器每個頁面都會多帶帶起一個進程,頁面1的主線程被阻塞并不會影響影響頁面2的主線程
之前在Stackoverflow看了一個答案,感覺還比較靠譜
JavaScript Engine:parse your code and convert it to runnable commands
JavaScript Runtime Environment :provide some objects to javascript so that it can interact with the outside world.
For example, the Chrome Browser and node.js use the same Engine - V8, but their Runtimes are different: in Chrome you have the window, DOM objects etc, while node gives you require, Buffers and processes.
通俗的講,上面這張圖,左邊你可以看成JS引擎,右邊你可以看成JS運行環(huán)境
Eventloop之前已經(jīng)說了,JS在設(shè)計之初選擇單線程,是以為單線程簡單,可控。
但是單線程存在一個問題,部分任務(wù)比如Ajax請求數(shù)據(jù),如果設(shè)計成同步的,后面的任務(wù)將都去等待Ajax請求完,這個性能是不能接受的。
所以瀏覽器內(nèi)核(?個人推測,暫時沒有找到相關(guān)資料)將任務(wù)分為同步任務(wù)和異步任務(wù),所有同步任務(wù)放到主線程上執(zhí)行,形成一個執(zhí)行棧(execution context stack)。所以異步任務(wù)放到其他異步線程上去執(zhí)行。
當異步任務(wù)執(zhí)行完以后,相關(guān)回調(diào)函數(shù)會放入到消息隊列(也有叫callback queue、task queue)中。
主線程同步任務(wù)執(zhí)行完,每個一段事件會檢查消息隊列一次,有回調(diào)函數(shù)就會執(zhí)行,如此往復(fù)就成為Eventloop
個人的理解 :JS引擎是同步的,瀏覽器通過eventloop這種機制實現(xiàn)了異步
看一下How JavaScript works 怎么描述這個過程的
So, for example, when your JavaScript program makes an Ajax request to fetch some data from the server, you set up the “response” code in a function (the “callback”), and the JS Engine tells the hosting environment:宏任務(wù),微任務(wù)、練習(xí)
“Hey, I’m going to suspend execution for now, but whenever you finish with that network request, and you have some data, please call this function back.”The browser is then set up to listen for the response from the network, and when it has something to return to you, it will schedule the callback function to be executed by inserting it into the event loop.
面試喜歡考宏任務(wù)(macrotask),微任務(wù)(microtask)。那么我們就來講一講macrotask和microtask是個啥子
宏任務(wù)又成為task。可以理解是每次執(zhí)行棧執(zhí)行的代碼就是一個task,task1->渲染->task1
microtask,可以理解是在當前 task 執(zhí)行結(jié)束后立即執(zhí)行的任務(wù),所以microtask有歸屬性,只在對應(yīng)的task執(zhí)行完后立即執(zhí)行.task1->microtask1->渲染->task2->microtask2->渲染...
macrotask:主代碼塊,setTimeout,setInterval等(可以看到,事件隊列中的每一個事件都是一個macrotask)microtask:Promise,process.nextTick等
求下面代碼的結(jié)果
console.log("1"); setTimeout(function() { //回調(diào)2 new Promise(function(resolve) { console.log("2"); resolve(); }).then(function() { console.log("3") }) console.log("4"); },2000) new Promise(function(resolve) { console.log("5"); resolve(); }).then(function() { console.log("6") }) setTimeout(function() { //回調(diào)1 new Promise(function(resolve) { console.log("7"); resolve(); }).then(function() { console.log("8") }) setTimeout(function(){ //回調(diào)3 console.log("9") },2000) },1000) //(156) (78) (243) (9)
解析:
task1: 輸出1 5 ----> microtask1 輸出6 --(執(zhí)行棧空)-->render---->eventloop
1秒以后 callback queue里面加入回調(diào)1 被eventloop捕獲,同步任務(wù)入棧,異步任務(wù)給settiomeout線程(也就是回調(diào)3的那個異步任務(wù))
task2: 輸出7 ----> microtask2 輸出8 --(執(zhí)行棧空)-->render---->eventloop
2秒以后 callback queue里面加入回調(diào)2 被eventloop捕獲,同步任務(wù)入棧
task3: 輸出2 4 ----> microtask3 輸出3 --(執(zhí)行棧空)-->render---->eventloop
3秒以后 callback queue里面加入回調(diào)3 被eventloop捕獲,同步任務(wù)入棧
task4: 輸出9 --(執(zhí)行棧空)-->render---->eventloop...
let fs = require("fs"); fs.readFile("./1.js","utf-8",(err,data)=>{ // fs.readFile("./2.js","utf-8",(err,data)=>{ // fs.readFile("./3.js","utf-8",(err,data)=>{ // }) }) })
缺點是容易形成回調(diào)地獄,不能return
promiseconst fs = require("fs"); const readFile(i) = new Promise(function(){ fs.readFile(`./${i}.js`,"utf-8",(err,data)=>{ resolve(data) }) }) readFile(1) .then(readFile(2)) .then(readFile(3)) .....async await
async function read(){ //await后面必須跟一個promise, let a = await readFile("./1.txt"); console.log(a); let b = await readFile("./2.txt"); console.log(b); let c = await readFile("./3.txt"); console.log(c); return "end"; }尾聲
以上是我看了多篇文章以后,結(jié)合自己的理解,對javascript異步單線程,以及運行機制做的一個總結(jié)。如果你感覺哪一部分有點問題,歡迎在評論區(qū)留言。
參考從瀏覽器多進程到JS單線程,JS運行機制最全面的一次梳理
JavaScript 運行機制詳解:再談Event Loop
JavaScript異步機制詳解
JavaScript 運行原理解析
What is the difference between JavaScript Engine and JavaScript Runtime Environment
并發(fā)模型與事件循環(huán)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/109828.html
摘要:如果看完本文后,還對進程線程傻傻分不清,不清楚瀏覽器多進程瀏覽器內(nèi)核多線程單線程運行機制的區(qū)別。因此準備梳理這塊知識點,結(jié)合已有的認知,基于網(wǎng)上的大量參考資料,從瀏覽器多進程到單線程,將引擎的運行機制系統(tǒng)的梳理一遍。 前言 見解有限,如有描述不當之處,請幫忙及時指出,如有錯誤,會及時修正。 ----------超長文+多圖預(yù)警,需要花費不少時間。---------- 如果看完本文后,還...
摘要:任務(wù)隊列分同步任務(wù)異步任務(wù),異步任務(wù)要掛起,同步任務(wù)全部執(zhí)行完再執(zhí)行異步任務(wù)。執(zhí)行棧執(zhí)行的是同步任務(wù)。什么時候開啟異步任務(wù)和事件的,先注冊函數(shù)體,觸發(fā)時放入異步任務(wù)隊列,再執(zhí)行點擊某一按鈕瀏覽器卡死中的。 1.對比同步和異步 使用異步的場景: 定時任務(wù):setTimeout、setInterval 網(wǎng)絡(luò)請求:ajax請求、動態(tài)加載 事件綁定 //同步 console.log(1...
摘要:而異步則是相反,調(diào)用在發(fā)出之后,這個調(diào)用就直接返回了,所以沒有返回結(jié)果而是在調(diào)用發(fā)出后,被調(diào)用者通過狀態(tài)通知來通知調(diào)用者,或通過回調(diào)函數(shù)處理這個調(diào)用。總結(jié)回調(diào)函數(shù)是異步編程中的基石,但同時也存在很多問題,不太適合人類自然語言的線性思維習(xí)慣。 為什么 JS 是單線程? 眾所周知,Javascript 語言的執(zhí)行環(huán)境是單線程(single thread)。 所謂單線程,就是指一次只能完成一...
摘要:引擎線程,也稱為內(nèi)核,負責處理腳本程序,例如引擎。異步請求線程,也就是發(fā)出請求后,接收響應(yīng)檢測狀態(tài)變更等都是這個線程管理的。為了解決這個問題,提出標準,允許腳本創(chuàng)建多個線程,但是子線程完全受主線程控制,且不得操作。 本文主要參閱了以下兩篇文章,對JS的Event Loop運行機制基礎(chǔ)知識進行了整理。從瀏覽器多進程到JS單線程,JS運行機制最全面的一次梳理JavaScript 運行機制詳...
摘要:異步任務(wù)必須指定回調(diào)函數(shù),當異步任務(wù)從任務(wù)隊列回到執(zhí)行棧,回調(diào)函數(shù)就會執(zhí)行。事件循環(huán)主線程從任務(wù)隊列中讀取事件,這個過程是循環(huán)不斷的,所以整個的這種運行機制又稱為。事件循環(huán)事件循環(huán)是指主線程重復(fù)從消息隊列中取消息執(zhí)行的過程。 參考鏈接:這一次,徹底弄懂 JavaScript 執(zhí)行機制https://zhuanlan.zhihu.com/p/...從瀏覽器多進程到JS單線程,JS運行機制...
閱讀 3197·2021-09-06 15:02
閱讀 2243·2019-08-30 15:48
閱讀 3439·2019-08-29 11:08
閱讀 3281·2019-08-26 13:55
閱讀 2440·2019-08-26 13:35
閱讀 3162·2019-08-26 12:11
閱讀 2598·2019-08-26 11:48
閱讀 881·2019-08-26 11:42