瀏覽器UI多執行緒及對JavaScript單執行緒底層運作機制的理解
早在我剛剛學習JavaScript的時候,我就被「灌輸」了這樣的想法
JavaScript是單執行緒的
可是在我不斷的學習過程中
學到了定時器、ajax的非同步載入
一度讓我對這句話產生懷疑
既然JavaScript是單執行緒的,為什麼它還存在非同步載入?
後來我知道了瀏覽器中不只有js一個線程,它與其他線程共同組成了—– 瀏覽器UI多線程
早就想寫一篇這樣的文章,只是感覺理解的還不夠,怕我寫的對大家產生誤導
看完網上大家寫的各種博文,更是一臉懵逼
不過最後還是鼓起勇氣決定來談一下╮(╯_╰)╭
那麼現在拋開JavaScript線程的問題不談,我們先來看看什麼是瀏覽器UI線程
瀏覽器UI多線程
#先來上一張圖
一般瀏覽器至少有三個常駐執行緒
##GUI渲染執行緒(渲染頁)
JS引擎執行緒(處理腳本)
- ##事件觸發執行緒
(控制互動)
包括瀏覽器有時候會開闢新的執行緒例如用完就丟的Http請求執行緒(Ajax)等等其他執行緒
這些線程在UI線程的控制下井然有序的工作著
關於這個常駐線程網上觀點不一致,各個瀏覽器實現可能也不一樣,這裡就不深考究了
雖然我把js引擎線程放在了右下角,但是它是瀏覽器的主線程
道理很簡單,因為它們都要操作DOM,如果js線程想要某個DOM的樣式,渲染引擎必須停止工作(霸道總裁讓你站那兒別動)
js單線程
為什麼JavaScript是單執行緒
單執行緒就是同一時間只能做一件事那麼JavaScript多執行緒不好嗎,那效率多高啊
不好
js設計出來就是為了與使用者交互,處理DOM
如果JavaScript多線程了,那就必須處理多線程同步的問題(依稀記得曾經被C++線程同步支配的恐怖)
假如js是多線程,同一時間一個線程想要修改DOM,另一個執行緒想要刪除DOM
問題就變得複雜許多,瀏覽器不知道聽誰的,如果引入「鎖」的機制,那就麻煩死了(那我就不學前端了( ̄_,  ̄ ))
所以我們這樣一個腳本語言根本沒有必要搞得那麼複雜,所以JavaScript誕生起就是單線程執行
雖然H5提出了Web Worker,但是它不能夠操作DOM,還是要委託個大哥js主線程解決
這些子線程完全受主線程大哥控制的,所以沒有改變JavaScript單線程的本質
執行堆疊
我們先來看看什麼是執行堆疊
堆疊是先進後出(FILO)的資料結構執行堆疊中存放正在執行的任務,每一個任務叫做「幀」
舉例
function foo(c){ var a = 1; bar(200); }function bar(d){ var b = 2; } foo(100);
我們來看看執行堆疊發生了怎樣的變化
- 最開始,程式碼沒有執行的時候,執行堆疊為空堆疊
- foo函數執行時,創建了一幀,這幀中包含了形參、局部變數(預編譯過程),然後把這一幀壓入棧中
- 然後執行foo函數內程式碼,執行bar函數
- #建立新幀,同樣有形參、局部變量,壓入堆疊中
- bar函數執行完畢,彈出堆疊
- foo函式執行完畢,彈出堆疊
- 執行堆疊為空
- 執行堆疊其實相當於js主執行緒
任務佇列
佇列是先入先出(FIFO)的資料結構
js執行緒中還存在著一個任務佇列任務佇列包含了一系列待處理的任務
單一執行緒就意味著所有任務需要一個接一個的執行,如果一個任務執行的時間太長,那後面的任務就得等著
就好比護士阿姨給排隊的小朋友打針,如果最前面的小朋友一直滾針,那就一直扎,後面的小朋友就得等著(這比喻好像不恰當)
可是如果最前面的小朋友暈針昏倒了
那麼護士阿姨不可能坐那裡了等到他醒來,一定是先給後面的小朋友扎針
也就是相當於把那位小朋友「掛起」(非同步)
所以,任務可以分成兩種
- 同步任務
- 非同步任務
同步任务就是正在主线程执行栈中执行的任务(在屋子内打针的小朋友)
而异步任务是在任务队列等候处理的任务(在屋子外等候打针的小朋友)
一旦执行栈中没有任务了,它就会从执行队列中获取任务执行
事件与回调
任务队列是一个事件的队列,IO设备(输入/输出设备)每完成一项任务,就会在任务队列中添加事件处理
用户触发了事件,也同样会将回调添加到任务队列中去
主线程执行异步任务,便是执行回调函数(事件处理函数)
只要执行栈一空,排在执行队列前面的会被优先读取执行,
不过主线程会检查时间,某些事件需要到了规定时间才能进入主线程处理(定时器事件)
事件循环
主线程从执行队列不断地获取任务,这个过程是循环不断地,叫做“Event Loop”事件循环
同步任务总是会在异步任务之前执行
只有当前的脚本执行完,才能够去拿任务队列中的任务执行
前面也说到了,任务队列中的事件可以是定时器事件
定时器分为两种 setTimeout() 和 setInterval()
前者是定时执行一次,后者定时重复执行
第一个参数为执行的回调函数,第二个参数是间隔时间(ms)
来看这样一个例子
setTimeout(function(){ console.log('timer'); },1000);console.log(1);console.log(2);console.log(3);
这个没什么问题,浏览器打印的是 1 2 3 timer
但是这样呢
setTimeout(function(){ console.log('timer'); },0);//0延时console.log(1); console.log(2); console.log(3);
浏览器打印依然打印的是 1 2 3 timer
也许有同学知道,旧版浏览器,setTimeout定时至少是10ms(即便你设置了0ms),
H5新规范是定时至少4ms(我读书少不知道为什么),改变DOM也是至少16ms
也许这是因为这个原因
那么我再改动一下代码
setTimeout(function(){ console.log('timer'); },0);var a = +new Date();for(var i = 0; i < 1e5; i++){ console.log(1); }var b = +new Date(); console.log(b - a);
这回够刺激了吧,输出10w次,我浏览器都假死了(心疼我chrome)
不仅如此,我还打印了循环所用时间
来看看控制台
输出了10w个1,用了将近7s
timer依然是最后打印的
这就证明了我前面说的话: 同步任务总是会在异步任务之前执行
只有我执行栈空了,才会去你任务队列中取任务执行
实例
最后我举一个例子加深一下理解
demo.onclick = function(){ console.log('click'); }function foo(a){ var b = 1; bar(200); }function bar(c){ var d = 2; click//伪代码 此时触发了click事件(这里我假装程序运行到这里手动点击了demo) setTimeout(function(){ console.log('timer'); }, 0); } foo(100);
怕大家蒙我就不写Ajax了
Ajax如果处理结束后(通过Http请求线程),也会将回调函数放在任务队列中
还有一点click那一行伪代码我最开始是想用demo.click()模拟触发事件
后来在测试过程中,发现它好像跟真实触发事件不太一样
它应该是不通过触发事件线程,而是存在于执行栈中,就相当于单纯地执行click回调函数
不过这只是我自己的想法有待考证,不过这不是重点,重点是我们理解这个过程,请大家不要吐槽我╰( ̄▽ ̄)╭
下面看看执行这段代码时发生了什么(主要说栈和队列的问题,不会赘述预编译过程)
主线程开始执行,产生了栈、堆、队列
demo节点绑定了事件click,交给事件触发线程异步监听
执行foo函数(之前同样有预编译过程),创建了帧包括foo函数的形参、局部变量压入执行栈中
foo函数内执行bar函数,创建帧包括bar函数的形参、局部变量压入执行栈中
触发了click事件,事件触发线程将回调事件处理函数放到js线程的任务队列中
触发了定时器事件,事件触发线程立即(4ms)将回调处理函数放到js线程的任务队列中
bar函数执行完毕,弹出栈
foo函数执行完毕,弹出栈
此时执行栈为空
执行栈向任务队列中获取一个任务:click回调函数,输出‘click’
执行栈项任务队列中获取一个任务:定时器回调函数,输出‘timer’
执行结束
这里从任务队列里不断取任务的过程就是Event Loop
有一些我的理解,如果发现不对或者有疑问的地方,请联系我
相信大家看了这个例子应该对js底层运行机制有了一个大概的了解
以上就是瀏覽器UI多執行緒及對JavaScript單執行緒底層運作機制的理解及对JavaScript单线程底层运行机制的理解的内容,更多相关内容请关注PHP中文网(www.php.cn)!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

用法:在JavaScript中,insertBefore()方法用於在DOM樹中插入一個新的節點。這個方法需要兩個參數:要插入的新節點和參考節點(即新節點將要插入的位置的節點)。

JavaScript是一種廣泛應用於Web開發的程式語言,而WebSocket則是一種用於即時通訊的網路協定。結合二者的強大功能,我們可以打造一個高效率的即時影像處理系統。本文將介紹如何利用JavaScript和WebSocket來實作這個系統,並提供具體的程式碼範例。首先,我們需要明確指出即時影像處理系統的需求和目標。假設我們有一個攝影機設備,可以擷取即時的影像數
