本文引用至: web worker
因爲瀏覽器的限制,註定了每一個網頁只能在一個進程程當中運行, 並且,js又只能運行在一個線程當中. 因此, 做爲一名開發者來講, 對於這樣的結果就只能呵呵了. 若是你想進行高複雜度的運算, 基本上就能夠go die了(只要運行, 你網頁基本上就崩掉了). 固然,聰明的W3C早就知道developer內心的小貓膩. 推出了web worker 這個概念. 咱們接下來,來正式接觸一下 web worker吧.html
web worker 既然是一個線程. 那一定會設計到線程間的通訊. 這裏,ww(web worker)提供了一個最簡單的方法--postMessage(msg) 進行雙向通訊.
來看一個簡單的Demo:react
// index.html 中的 main.js var worker = new Worker('Worker.js'); worker.addEventListener('message', function(e) { console.log('Worker said: ', e.data); }, false); worker.postMessage('Hello World'); // Send data to our // Worker.js內容 self.addEventListener('message', function(e) { self.postMessage(e.data); }, false);
當載入main.js時, 在Console裏,就會出現Hello World
的內容. 上面的例子實際上,已經說明了worker的工做原理. 在worker中,self就是 new Wroker的實例化的內容.
不過, 這裏想強調一點, 經過postMessage傳遞的msg並非兩個線程共享的.(要是共享的,不就GG了) 傳遞的Msg其實是一個副本, 最具備表明性的,應該就算是Object.jquery
// main.js 傳遞一個Object var worker = new Worker('Worker.js'); worker.addEventListener('message', function(e) { console.log('Worker said: ', msg.a); }, false); var msg = { a:2 } worker.postMessage(msg); // worker.js 接受,並返回 self.addEventListener('message', function(e) { e.data.a=3; self.postMessage(e.data); }, false); // 最後返回的結果是2
在一端向另一端傳遞msg時, 中間會通過serialized, 而後de-serialized 最終獲得結果. 通俗一點就是:web
// 傳遞前 JSON.stringify(msg); // 解析數據 JSON.parse(msg);
當worker已經處理完畢,沒有多大卵用以後. 就能夠kill掉該線程.ajax
在web中, 提供了兩種方法來關閉Web Worker. 關閉指定的Worker以後, 至關於即,kill 掉該線程. 因此, 這裏須要注意一下:瀏覽器
worker.terminate(): 在外部終結該worker.併發
self.close(): 在worker內部自動終結.dom
官方推薦是,使用self.close進行內部的自動關閉. 這樣能防止, 意外關閉正在運行的worker.函數
上面,在worker.js中,咱們使用self來獲取worker自帶的方法.post
self.addEventListener('message', function(e) { self.postMessage(e.data); }, false);
實際上, 在worker中, 他的全局索引就是self和this. 因此, 上面的代碼能夠簡寫爲:
addEventListener('message', function(e) { postMessage(e.data); }, false);
worker 引用的就是js文件, 可能有些童鞋就會將worker當成通常js來使用. 可是,因爲worker是獨立的線程緣由,他和main js threading仍是有很大區別的.
他可以訪問的權限有:
The navigator object: window.navigator 相關屬性和方法
The location object (read-only): 只讀的window.location內容.
XMLHttpRequest: 臥槽... 能夠訪問這個那就不得了了. worker就能夠利用ajax來和後臺進行通訊了.
setInterval()相關時間函數
剩下的就是不能訪問的了。
web worker 中的error handler和window處理的方式,也是使用error時間進行監聽.
worker.onerror = function(e){ throw new Error(e.message + " (" + e.filename + ":" + e.lineno + ")"); };
worker在訪問時, 只能是在同一host下才行. 即, 你的worker只能處於指定目錄下的path中。
// 這種狀況下,就沒法訪問worker new Worker('http://crossdomain.com/worker.js');
另外, 若是你使用的是本地調試file://xxx
的話, 也不能使用worker.
在一個worker裏面能夠再spawn出其餘的worker. 使用方法和在main.js中同樣.
// 加載worker.js var sub_worker = new Worker('subworker.js');
subworker和worker有這一樣的限制, 同域, 而且他的路由是相對於parent worker. 來看一個demo吧:
// main.js var worker = new Worker('worker.js'); worker.onmessage = function (event) { document.getElementById('result').textContent = event.data; }; // worker.js // 用來進行遍歷計算 var num_workers = 10; var items_per_worker = 1000000; // start the workers var result = 0; var pending_workers = num_workers; for (var i = 0; i < num_workers; i += 1) { var worker = new Worker('subworker.js'); worker.postMessage(i * items_per_worker); worker.postMessage((i+1) * items_per_worker); worker.onmessage = storeResult; } // handle the results function storeResult(event) { result += 1*event.data; pending_workers -= 1; if (pending_workers <= 0) postMessage(result); // finished! } // subworker.js var start; onmessage = getStart; function getStart(event) { start = 1*event.data; onmessage = getEnd; } var end; function getEnd(event) { end = 1*event.data; onmessage = null; work(); } function work() { var result = 0; for (var i = start; i < end; i += 1) { // perform some complex calculation here result += 1; } postMessage(result); close(); }
另外,若是你想在當前的worker裏面加載其餘庫文件, 就可使用importScripts
來導入.
// 導入其餘庫的文件 importScripts('jquery.js','react.js','react-dom.js');
根據worker 獨立線程這一特性. 他的使用場景也很是清晰了.反正什麼大規模數據併發,I/O操做的.均可以交給他來進行. 總的來講有一下幾種場景:
懶加載數據
文本分析
流媒體數據處理
web database的更新
大量JSON返回數據的處理
除了你們所熟知的web worker, 或者更確切的來講--Dedicated workers.
總的來講web worker分爲兩種:
Dedicated worker (DW): 即便用 new Worker()來建立的. 該worker通常只能和creator進行通訊. 即, 在建立worker的js script中才能使用.
Shared Wrokers (SW): 使用new SharedWorker() 進行建立. 他能在不一樣的js script中使用.
具體來說SW和DW的區別就是一個只能在一個script中使用. 一個能夠在不一樣的script中使用.
看一個簡單demo:
// index.html 發起shared worker 通訊 <script> var worker = new SharedWorker('sharedWorker.js'); worker.port.addEventListener("message", function(e) { console.log(e.data); }, false); worker.port.start(); // post a message to the shared web worker console.log("Calling the worker from script 1"); worker.port.postMessage("script-1"); </script> <script> console.log("Calling the worker from script 2"); worker.port.postMessage("script-2"); </script> // sharedWorker.js 內 var connections = 0; self.addEventListener("connect", function (e) { var port = e.ports[0]; connections++; port.addEventListener("message", function (e) { port.postMessage("Welcome to " + e.data + " (On port #" + connections + ")"); }, false); port.start(); }, false);
不過, SW的兼容性比較差, 能真正在實踐場景使用的地方仍是少的. 因此,這裏也只是當作瞭解.
SW 和 DW 同樣, 也有一些features:
映入外部文件: importScripts()
錯誤監聽: error事件監聽
關閉通訊: port.close()
ajax交互: 有權訪問xmlHttpRequest對象
能訪問navigator object
訪問 location object
setTimeout等時間函數