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);
下面來看一下詳細流程:
1. worker.js的異步加載過程如下圖,加載是在建立WebCore::JSWorker和WebCore::Worker對象的時候發起的,并且是異步加載的流程,不會阻塞後續JS的執行,這也是為什麼首次調用worker.postMessage()的時候,會出現worker線程還沒有建立的情況;
2. worker線程的建立如下圖所示。可以看出,當worker.js加載完成後,WebKit會通過中轉對象WorkerMessagingProxy建立DedicatedWorkerThread對象,并啟動WorkerThread; WorkerMessagingProxy保持DedicatedWorkerThread對象的指針;
3. 主線程向worker線程發送消息的流程如下,當js執行到"worker.postMessage()“時,最終會通過JS主線程虛拟機映射到JSWorker::postMessage()函數,并通過中轉對象WorkerMessagingProxy将消息添加到worker的消息隊列; 如果worker線程在發送消息的時候,還沒有建立,我們看到有個m_queueEarlyTasks對象會臨時儲存目前消息,并在worker線程建立後再轉移到正式的消息隊列中; 否則,直接将消息添加到WorkerRunLoop管理的正式消息隊列中;
4. worker線程中處理消息的流程如下,這就是worker.js中開始執行”onmessage()”的流程; 我們可以看到DedicatedWorkThread對象在自己的線程環境下的runLoop取出消息隊列中的資料執行;而執行是通過EventTarget::dispatchEvent()分發并fire一個"message"類型的事件來實作的;
.
5. 下圖是在worker線程中發送消息的流程,也是通過WorkerMessagingProxy來進行中傳,最後會觸發Document::postTask()函數,該函數實際上将Document::didReceiveTask()函數抛到主線程上去執行;
6. 下圖是main線程上處理消息的流程, Document::didReceiveTask()在主線程上開始執行,最終也是通過dispatch并fire一個"message"類型的事件實作消息的處理;
,
是以總得來講,woker的機制是通過中轉對象實作消息的傳遞,再通過"message"事件完成消息的處理;
(轉載請注明出處:http://blog.csdn.net/codigger)