首頁 > web前端 > js教程 > 主體

瀏覽器的多執行緒機制詳解

php中世界最好的语言
發布: 2018-03-16 15:20:21
原創
1565 人瀏覽過

這次帶給大家瀏覽器的多執行緒機制詳解,瀏覽器的多執行緒機制使用的注意事項有哪些,下面就是實戰案例,一起來看一下。

在講之前,大家都知道js是基於單執行緒的,而這個執行緒就是瀏覽器的js引擎。
先來看看大家用的瀏覽器都有那些執行緒吧。

假如我們要執行一些耗時的操作,例如載入一張很大的圖片,我們可能需要一個進度條來讓使用者進行等待,在等待的過程中,整個js執行緒會被阻塞,後面的程式碼無法正常運行,這可能大大的降低使用者體驗,這時候我們就期望擁有一個工作執行緒來處理這些耗時的操作。在傳統的html時代是基本上不可能實現的,而現在,我們擁有一種叫做worker的東西。它是js裡的一個類,而我們只需要創建它的實例就可以使用它。

var worker = new Worker(js file path);
登入後複製

建構函數的參數填入你的js檔案的路徑,這個js檔案將會在瀏覽器新開的執行緒裡運行,而與原先的js引擎的執行緒並不影響。

那麼既然互不影響,兩個線程之間要怎麼來聯繫呢​​,答案其實已經在程式碼裡了,那就是onPostMessage 和onmessage這兩個函數,其中onPostMessage(data)的參數是你要傳遞的數據,而onmessage是一個回呼函數,只有在接受到數據時,onmessage會被回調,onmessage有一個隱藏的參數,那就是event,我們可以用event.data獲取到傳遞過來的資料來更新主執行緒。

JavaScript的setTimeout與setInterval是兩個很容易欺騙別人感情的方法,因為我們開始常常以為調用了就會按既定的方式執行, 我想不少人都深有同感, 例如

[javascript] view plain copy print?瀏覽器的多執行緒機制詳解瀏覽器的多執行緒機制詳解

  1. setTimeout( function() { alert('你好!'); } , 0);  

  2. #setInterval( callbackFunction , 100);  

認為setTimeout中的問候方法會立即被執行,因為這並不是憑空而說,而是JavaScript API文檔明確定義第二個參數意義為隔多少毫秒後,回調方法就會被執行. 這裡設為0毫秒,理所當然就立即被執行了.
同理對setInterval的callbackFunction方法每間隔100毫秒就立即被執行深信不疑!

但隨著JavaScript應用開發經驗不斷的增加和豐富,有一​​天你發現了一段怪異的程式碼而百思不得其解:

[javascript] view plain copy print?瀏覽器的多執行緒機制詳解瀏覽器的多執行緒機制詳解

  1. #p.onclick = function(){  

  2.     setTimeout( function(){document.getElementById('inputField').focus();}, ent.getElementById('inputField').focus();}, 0);

  3. #};  

#既然是0毫秒後執行,那麼還用setTimeout幹什麼, 此刻, 堅定的信念已開始動搖.

直到最後某一天, 你不小心寫了一段糟糕的程式碼:

[javascript] view plain copy print?瀏覽器的多執行緒機制詳解瀏覽器的多執行緒機制詳解

  1. setTimeout( function(){ while(true){} } , 100);  

  2. setTimeout( function(){ alert('你好! '); } , 200);  

  3. #setInterval( callbackFunction , 200);  

第一行程式碼進入了死循環迴圈,但不久你就會發現,第二,第三行並不是預料中的事情,alert問候未見出現,callbacKFunction也杳無音訊!

這時你徹底迷茫了,這種情景是難以接受的,因為改變長久以來既定的認知去接受新思想的過程是痛苦的,但情事實擺在眼前,對JavaScript真理的探求並不會因為痛苦而停止,下面讓我們來展開JavaScript線程和定時器探索之旅!

拔除雲霧見月明

#出現上面所有誤解的最主要一個原因是:潛意識中認為,JavaScript引擎有多個執行緒在執行,JavaScript的定時器回呼函數是異步執行的.

而事實上的,JavaScript使用了障眼法,在多數時候騙過了我們的眼睛,這裡背光得澄清一個事實:

JavaScript引擎是單執行緒運行的,瀏覽器無論在什麼時候都只且只有一個執行緒在執行JavaScript程式.

JavaScript引擎使用單執行緒運行也是有意義的,單執行緒不必理會執行緒同步這些複雜的問題,問題得到簡化.

那麼單執行緒的JavaScript引擎是怎麼配合瀏覽器核心處理這些定時器和回應瀏覽器事件的呢?
下面結合瀏覽器內核處理方式簡單說明.

瀏覽器內核實現允許多個線程異步執行,這些線程在內核制控下相互配合以保持同步.假如某一瀏覽器內核的實作至少有三個常駐線程:javascript引擎線程,介面渲染線程,瀏覽器事件觸發線程,除些以外,也有一些執行完就終止的線程,如Http請求線程,這些非同步線程都會產生不同的非同步事件,下面透過一個圖來闡明單線程的JavaScript引擎與另外那些線程是怎樣互動通信的.雖然每個瀏覽器內核實現細節不同,但這其中的調用原理都是大同小異.

由圖可看出,瀏覽器中的JavaScript引擎是基於事件驅動的,這裡的事件可看作是瀏覽器派給它的各種任務,這些任務可以源自JavaScript引擎目前執行的程式碼區塊,如呼叫setTimeout新增一個任務,也可來自瀏覽器內核的其它執行緒,如介面元素滑鼠點擊事件,定時觸發器時間到達通知,非同步請求狀態變更通知等.從程式碼角度看來任務實體就是各種回呼函數,JavaScript引擎一直等待著任務佇列中任務的到來.由於單執行緒關係,這些任務得進行排隊,一個接著一個被引擎處理.

上圖t1-t2..tn表示不同的時間點,tn下面對應的小方塊代表該時間點的任務,假設現在是t1時刻,引擎運行在t1對應的任務方塊代碼內,在這個時間點內,讓我們來描述一下瀏覽器內核其它線程的狀態.

t1時刻:

#GUI渲染線程:

該線程負責渲染瀏覽器介面HTML元素,當介面需要重繪(Repaint)或由於某種操作引發回流(reflow)時,該線程就會執行.本文雖然重點解釋JavaScript定時機制,但這時有必要說說渲染執行緒,因為該執行緒與JavaScript引擎執行緒是互斥的,這容易理解,因為JavaScript腳本是可操縱DOM元素,在修改這些元素屬性同時渲染介面,那麼渲染執行緒前後獲得的元素資料就可能不一致了.

在JavaScript引擎執行腳本期間,瀏覽器渲染執行緒都是處於掛起狀態的,也就是說被」凍結」了.

所以,在腳本中執行對介面進行更新操作,如添加結點,刪除結點或改變結點的外觀等更新並不會立即體現出來,這些操作將保存在一個隊列中,待JavaScript引擎空閒時才有機會渲染出來.

GUI事件觸發線程:

JavaScript腳本的執行不影響html元素事件的觸發,在t1時間段內,首先是用戶點擊了一個滑鼠鍵,點擊被瀏覽器事件觸發線程捕捉後形成一個滑鼠點擊事件,由圖可知,對於JavaScript引擎線程來說,這事件是由其它線程異步傳到任務隊列尾的,由於引擎正在處理t1時的任務,這個滑鼠點擊事件正在等待處理.

定時觸發線程:

注意這裡的瀏覽器模型定時計數器並不是由JavaScript引擎計數的,因為JavaScript引擎是單線程的,如果處於阻塞線程狀態就計不了時,它必須依賴外部來計時並觸發定時,所以隊列中的定時事件也是非同步事件.

由圖可知,在這t1的時間段內,繼滑鼠點擊事件觸發後,先前已設定的setTimeout定時也到達了,此刻對JavaScript引擎來說,定時觸發線程產生了一個非同步定時事件並放到任務隊列中, 該事件被排到點擊事件回調之後,等待處理.
同理, 還是在t1時間段內,接下來某個setInterval定時器也被新增了,由於是間隔定時,在t1段內連續被觸發了兩次,這兩個事件被排到隊尾等待處理.

可見,假如時間段t1非常長,遠大於setInterval的定時間隔,那麼定時觸發線程就會源源不斷的產生異步定時事件並放到任務隊列尾而不管它們是否已被處理,但一旦t1和最先的定時事件前面的任務已處理完,這些排列中的定時事件就依序不間斷的被執行,這是因為,對於JavaScript引擎來說,在處理佇列中的各任務處理方式都是一樣的,只是處理的次序不同而已.

t1過後,也就是說目前處理的任務已回傳,JavaScript引擎會檢查任務佇列,發現目前佇列非空,就取出t2下面對應的任務執行,其它時間依此類推,由此看來:

#如果佇列非空,引擎就從佇列頭取出一個任務,直到該任務處理完,即返回後引擎接著運行下一個任務,在任務沒返回前隊列中的其它任務是沒法被執行的.

相信您現在已經很清楚JavaScript是否可多線程,也了解理解JavaScript定時器運行機制了,下面我們來對一些案例進行分析:

案例1:setTimeout與setInterval

[javascript] view plain copy print?瀏覽器的多執行緒機制詳解瀏覽器的多執行緒機制詳解

  1. setTimeout(function(){  

  2.    /* 程式碼區塊... */  

  3.    setTimeout(arguments.callee,

  4. ##}, 10);  


  5. setInterval(function(){  

  6.    /*程式碼區塊... */  

  7.  }, 10);  

#這兩段程式碼看一起效果一樣,其實非也,第一段中回呼函數內的setTimeout是JavaScript引擎執行後再設定新的setTimeout定時, 假定上一個回呼處理完到下一個回呼開始處理為一個時間間隔,理論兩個setTimeout回調執行時間間隔>=10ms.第二段自setInterval設定定時後,定時觸發線程就會源源不斷的每隔十秒產生非同步定時事件並放到任務佇列尾,理論上兩個setInterval回呼執行時間間隔

案例2:ajax非同步請求是否真的非同步?

#很多同學朋友搞不清楚,既然說JavaScript是單線程運行的,那麼XMLHttpRequest在連接後是否真的異步?

其實請求確實是異步的,不過這請求是由瀏覽器新開一個執行緒請求(參見上圖),當請求的狀態變更時,如果先前已設定回呼,這非同步執行緒就產生狀態變更事件放到JavaScript引擎的處理佇列中等待處理,當任務被處理時,JavaScript引擎始終是單線程運行回調函數,具體點即還是單線程運行onreadystatechange所設置的函數.

#相信看了本文案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章!

推薦閱讀:

單執行緒JS與多執行緒瀏覽器的使用

#關於js中類型轉換的一些小問題

#JS中的顯示類型轉換

js怎麼實作橫向捲動與浮動導航

以上是瀏覽器的多執行緒機制詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板