衆所周知,Javascript是運行在單線程環境中,也就是說沒法同時運行多個腳本。假設用戶點擊一個按鈕,觸發了一段用於計算的Javascript代碼,那麼在這段代碼執行完畢以前,頁面是沒法響應用戶操做的。可是,若是將這段代碼交給Web Worker去運行的話,那麼狀況就不同了:瀏覽器會在後臺啓動一個獨立的worker線程來專門負責這段代碼的運行,所以,頁面在這段Javascript代碼運行期間依然能夠響應用戶的其餘操做。javascript
Web Worker 是HTML5標準的一部分,這一規範定義了一套 API,它容許一段JavaScript程序運行在主線程以外的另一個線程中。
值得注意的是, Web Worker 規範中定義了兩類工做線程,分別是專用線程Dedicated Worker和共享線程 Shared Worker,其中,Dedicated Worker只能爲一個頁面所使用,而Shared Worker則能夠被多個頁面所共享。java
只需調用Worker() 構造函數並傳入一個要在 worker 線程內運行的腳本的URI,便可建立一個新的worker。web
var myWorker = new Worker("my_task.js");
// my_task.js中的代碼
var i = 0;
function timedCount(){
i = i+1;
postMessage(i);
setTimeout(timedCount, 1000);
}
timedCount();複製代碼
另外,經過URL.createObjectURL()建立URL對象,能夠實現建立內嵌的workerchrome
var myTask = ` var i = 0; function timedCount(){ i = i+1; postMessage(i); setTimeout(timedCount, 1000); } timedCount(); `;
var blob = new Blob([myTask]);
var myWorker = new Worker(window.URL.createObjectURL(blob));複製代碼
這樣,就能夠結合NEJ、Webpack進行模塊化管理、打包了。canvas
注意:傳入 Worker 構造函數的參數 URI 必須遵循同源策略。Worker線程的建立的是異步的,主線程代碼不會阻塞在這裏等待worker線程去加載、執行指定的腳本文件,而是會當即向下繼續執行後面代碼。瀏覽器
提示:本文全部的示例代碼都可直接拷貝到chrome控制檯中運行。框架
Worker 與其主頁面之間的通訊是經過 onmessage 事件和 postMessage() 方法實現的。異步
在主頁面與 Worker 之間傳遞的數據是經過拷貝,而不是共享來完成的。傳遞給 Worker 的對象須要通過序列化,接下來在另外一端還須要反序列化。頁面與 Worker 不會共享同一個實例,最終的結果就是在每次通訊結束時生成了數據的一個副本。mvvm
也就是說,Worker 與其主頁面之間只能單純的傳遞數據,不能傳遞複雜的引用類型:如經過構造函數建立的對象等。而且,傳遞的數據也是通過拷貝生成的一個副本,在一端對數據進行修改不會影響另外一端。模塊化
var myTask = ` onmessage = function (e) { var data = e.data; data.push('hello'); console.log('worker:', data); // worker: [1, 2, 3, "hello"] postMessage(data); }; `;
var blob = new Blob([myTask]);
var myWorker = new Worker(window.URL.createObjectURL(blob));
myWorker.onmessage = function (e) {
var data = e.data;
console.log('page:', data); // page: [1, 2, 3, "hello"]
console.log('arr:', arr); // arr: [1, 2, 3]
};
var arr = [1,2,3];
myWorker.postMessage(arr);複製代碼
前面介紹了簡單數據的傳遞,其實還有一種性能更高的方法來傳遞數據,就是經過可轉讓對象將數據在主頁面和Worker之間進行來回穿梭。可轉讓對象從一個上下文轉移到另外一個上下文而不會通過任何拷貝操做。這意味着當傳遞大數據時會得到極大的性能提高。和按照引用傳遞不一樣,一旦對象轉讓,那麼它在原來上下文的那個版本將不復存在。該對象的全部權被轉讓到新的上下文內。例如,當你將一個 ArrayBuffer 對象從主應用轉讓到 Worker 中,原始的 ArrayBuffer 被清除而且沒法使用。它包含的內容會(完整無差的)傳遞給 Worker 上下文。
var uInt8Array = new Uint8Array(1024*1024*32); // 32MB
for (var i = 0; i < uInt8Array .length; ++i) {
uInt8Array[i] = i;
}
console.log(uInt8Array.length); // 傳遞前長度:33554432
var myTask = ` onmessage = function (e) { var data = e.data; console.log('worker:', data); }; `;
var blob = new Blob([myTask]);
var myWorker = new Worker(window.URL.createObjectURL(blob));
myWorker.postMessage(uInt8Array.buffer, [uInt8Array.buffer]);
console.log(uInt8Array.length); // 傳遞後長度:0複製代碼
Worker 線程可以訪問一個全局函數imprtScripts()來引入腳本,該函數接受0個或者多個URI做爲參數。
瀏覽器加載並運行每個列出的腳本,每一個腳本中的全局對象都可以被 worker 使用。若是腳本沒法加載,將拋出 NETWORK_ERROR 異常,接下來的代碼也沒法執行。而以前執行的代碼(包括使用 window.setTimeout() 異步執行的代碼)依然可以運行。importScripts() 以後的函數聲明依然會被保留,由於它們始終會在其餘代碼以前運行。
注意:腳本的下載順序不固定,但執行時會按照傳入 importScripts() 中的文件名順序進行。這個過程是同步完成的;直到全部腳本都下載並運行完畢, importScripts() 纔會返回。
Worker執行的上下文,與主頁面執行時的上下文並不相同,最頂層的對象並非window,而是個一個叫作WorkerGlobalScope的東東,因此沒法訪問window、以及與window相關的DOM API,可是能夠與setTimeout、setInterval等協做。
WorkerGlobalScope做用域下的經常使用屬性、方法以下:
一、self
咱們可使用 WorkerGlobalScope 的 self 屬性來或者這個對象自己的引用
二、location
location 屬性返回當線程被建立出來的時候與之關聯的 WorkerLocation 對象,它表示用於初始化這個工做線程的腳步資源的絕對 URL,即便頁面被屢次重定向後,這個 URL 資源位置也不會改變。
三、close
關閉當前線程
四、importScripts
咱們能夠經過importScripts()方法經過url在worker中加載庫函數
五、XMLHttpRequest
有了它,才能發出Ajax請求
六、setTimeout/setInterval以及addEventListener/postMessage
在主頁面上調用terminate()方法,能夠當即殺死 worker 線程,不會留下任何機會讓它完成本身的操做或清理工做。另外,Worker也能夠調用本身的 close() 方法來關閉本身
// 主頁面調用
myWorker.terminate();
// Worker 線程調用
self.close();複製代碼
當 worker 出現運行時錯誤時,它的 onerror 事件處理函數會被調用。它會收到一個實現了 ErrorEvent 接口名爲 error的事件。該事件不會冒泡,而且能夠被取消;爲了防止觸發默認動做,worker 能夠調用錯誤事件的 preventDefault() 方法。
錯誤事件有三個實用的屬性:filename - 發生錯誤的腳本文件名;lineno - 出現錯誤的行號;以及 message - 可讀性良好的錯誤消息。
var myTask = ` onmessage = function (e) { var data = e.data; console.log('worker:', data); }; // 使用未聲明的變量 arr.push('error'); `;
var blob = new Blob([myTask]);
var myWorker = new Worker(window.URL.createObjectURL(blob));
myWorker.onerror = function onError(e) {
// ERROR: Line 8 in blob:http://www.cnblogs.com/490a7c32-7386-4d6e-a82b-1ca0b1bf2469: Uncaught ReferenceError: arr is not defined
console.log(['ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message].join(''));
}複製代碼
最後總結下Web Worker爲javascript帶來了什麼,以及典型的應用場景。
能夠加載一個JS進行大量的複雜計算而不掛起主進程,並經過postMessage,onmessage進行通訊,解決了大量計算對UI渲染的阻塞問題。
一、數學運算
Web Worker最簡單的應用就是用來作後臺計算,對CPU密集型的場景再適合不過了。
二、圖像處理
經過使用從<canvas>
中獲取的數據,能夠把圖像分割成幾個不一樣的區域而且把它們推送給並行的不一樣Workers來作計算,對圖像進行像素級的處理,再把處理完成的圖像數據返回給主頁面。
三、大數據的處理
目前mvvm框架愈來愈普及,基於數據驅動的開發模式也越愈發流行,將來大數據的處理也可能轉向到前臺,這時,將大數據的處理交給在Web Worker也是上上之策了吧。