天天看點

Web Worker在WebKit中的實作機制

    web worker 是運作在背景的 JavaScript,獨立于其他腳本,不會影響頁面的性能。這是HTML5的一個标準;實作上講,浏覽器為wokrer啟動了新的線程,進而實作了異步操作的功能;  

    下面是woker的一個簡單例子,在html頁面中,以worker.js為源檔案,建立了名為“worker”的Worker對象,通過worker.postMessage()接口向worker線程發送消息; worker線程将JSON格式化傳遞的兩個資料相加後,再通過postMessage()接口将消息發送回頁面js運作的主線程; 主線程再在onMessage()函數中處理woker線程發來的消息; 

main.html

<html>
  <head>
  <script type="text/javascript">
    var worker = new Worker('worker.js'); 
    var obj = {"first":1, "second":2};
    worker.postMessage(obj); 
    worker.onmessage = function (event) { 
      alert(event.data);
    }

    function postMsg(){
      if (worker)
        worker.postMessaage(obj); 
    }
  </script> 
  </head>
  <body>
     <button οnclick="postMsg()">post</button>
  </body>
</html>
           

worker.js

onmessage = function (event) 
 { 
    var data = event.data;
   var first=data.first;
   var second=data.second;
   handleTask(first,second); 
 }; 

 function  handleTask(a, b)
  {
    var out = a + b;
    postMessage("Worker Done! out = " + out);
  }
           

WebKit加載并執行js的流程簡單分成以下幾步:

1. 執行到"var worker = new Worker('worker.js')“時,在核心中構造WebCore::JSWorker對象(JSBbindings層)以及對應的WebCore::Worker對象(WebCore子產品); 

2.  構造JSWorker對象的過程中,根據初始化的url位址"worker.js"發起異步加載的流程; 

3.  執行worker.postMessage(),向worker線程發送JSON格式化的消息資料; 因為這個時候,worker線程還沒有建立,是以消息資料放在一個臨時消息隊列中; 

4. worker.js異步加載完成後,建立并啟動worker線程,并将臨時消息隊列中的消息資料copy到woker對應的WorkerRunLoop的消息隊列中; 

5. worker線程建立完成後,開始處理WorkerRunLoop的消息隊列中所儲存的消息; 

6. woker線程發送消息到主線程; 

7. 主線程收到worker線程發送的消息,執行onMessage(); 

在經過一輪消息來回後,我們如果通過例子中button按鈕來異步觸發消息發送,那麼步驟3的執行會有差別; 這個時候因為worker線程已經建立,是以消息會直接添加到WorkerRunLoop的消息隊列中; 

為了搞清楚整個實作機制,我們先來看一下WebKit内部worker相關的類, 其中Worker對應首頁面JS中的'worker'對象(主線程中),而DedicatedWorkerThread表示worker線程; WorkerMessagingProxy關聯了Worker對象與worker線程,進而實作主線程與worker線程之間的消息中轉; DedicatedWorkerThread通過WorkerScriptController控制worker.js檔案中的腳本在worker線程中的執行;浏覽器會為worker線程建立一個獨立的虛拟機環境(VM); 

Web Worker在WebKit中的實作機制

下面來看一下詳細流程: 

1. worker.js的異步加載過程如下圖,加載是在建立WebCore::JSWorker和WebCore::Worker對象的時候發起的,并且是異步加載的流程,不會阻塞後續JS的執行,這也是為什麼首次調用worker.postMessage()的時候,會出現worker線程還沒有建立的情況; 

Web Worker在WebKit中的實作機制

2. worker線程的建立如下圖所示。可以看出,當worker.js加載完成後,WebKit會通過中轉對象WorkerMessagingProxy建立DedicatedWorkerThread對象,并啟動WorkerThread; WorkerMessagingProxy保持DedicatedWorkerThread對象的指針; 

Web Worker在WebKit中的實作機制

3.  主線程向worker線程發送消息的流程如下,當js執行到"worker.postMessage()“時,最終會通過JS主線程虛拟機映射到JSWorker::postMessage()函數,并通過中轉對象WorkerMessagingProxy将消息添加到worker的消息隊列; 如果worker線程在發送消息的時候,還沒有建立,我們看到有個m_queueEarlyTasks對象會臨時儲存目前消息,并在worker線程建立後再轉移到正式的消息隊列中; 否則,直接将消息添加到WorkerRunLoop管理的正式消息隊列中; 

Web Worker在WebKit中的實作機制

4. worker線程中處理消息的流程如下,這就是worker.js中開始執行”onmessage()”的流程; 我們可以看到DedicatedWorkThread對象在自己的線程環境下的runLoop取出消息隊列中的資料執行;而執行是通過EventTarget::dispatchEvent()分發并fire一個"message"類型的事件來實作的; 

Web Worker在WebKit中的實作機制

5. 下圖是在worker線程中發送消息的流程,也是通過WorkerMessagingProxy來進行中傳,最後會觸發Document::postTask()函數,該函數實際上将Document::didReceiveTask()函數抛到主線程上去執行; 

Web Worker在WebKit中的實作機制

6. 下圖是main線程上處理消息的流程, Document::didReceiveTask()在主線程上開始執行,最終也是通過dispatch并fire一個"message"類型的事件實作消息的處理; 

, 

Web Worker在WebKit中的實作機制

是以總得來講,woker的機制是通過中轉對象實作消息的傳遞,再通過"message"事件完成消息的處理; 

(轉載請注明出處:http://blog.csdn.net/codigger)

繼續閱讀