##一.Web Workers 是什麼
Web Workers 是一種支持任務在後台線程運行的機制,沒錯,Web Worker 提供了行為受限的多線程
雖然瀏覽器中 js 的執行環境仍然是單線程的,但多線程在瀏覽器中早就存在了,比如異步 XHR 就是一個行為受限的線程,它獨立於主線程,通過內置的消息機制來通信,不存在一般多線程的併發問題
Web Workers 提供一個獨立於主線程的 context,在這個 context 裡可以使用受限的 API(禁止操作 DOM,可能無法訪問 Cache 等等)。多線程最大的好處就是不會阻塞 UI,可以用多線程來完成時耗巨大的任務(比如音頻/視頻解碼,以及在此基礎上的數據處理),甚至如果 API 允許的話,利用 Web Workers 就可以自行實現 XHR
P.S.是否允許 Web Worker 訪問 Cache,目前(2015/11/29)還有爭議,Chrome43 和 FF 支持,IE 和 Safari 支持性未知
一點廢話:Google 的 Gears 插件提��了 Worker Pool API,它就是 Web Workers 的「原型」,最初希望能夠增強瀏覽器的功能,比如支持離線瀏覽(離線訪問緩存頁面,重新上線後提交離線操作),但目前(2015/11/29)已經被棄用了
Google Gears API
The Google Gears API is no longer available. Thank you for your interest.
##二.Web Workers 有什麼用
後台線程,最大的作用就是 do stuff。支持後台線程意味著瀏覽器能夠承擔更多更重的任務了,可以把一部分原本由服務端承擔的運算(比如排序,編碼/解碼,模版生成)交給客戶端,以減輕服務端壓力,而不會影響用戶體驗(等待服務端返回結果和等待本地另一個線程返回結果一樣,都是等待,而且後者可能更快)
筆者認為 Web Workers 有以下適用場景:
- 音頻/視頻解碼
如果嘗試過audioContext.decodeAudioData之類的操作就會發現,我們迫切需要一個能「乾重活」的後台線程
- 圖片預處理
比如頭像上傳前的裁剪,甚至添加水印、拼合、添馬賽克,如果在客戶端能夠完成,就能避免大量的臨時文件傳輸
- 排序等數據處理算法
減輕服務器壓力
- 客戶端模版
比如 markdown,或者服務端返回 JSON,客戶端拿到後交給後台線程解析並應用模版 HTML 生成頁面,這些操作都由客戶端完成的話,需要傳輸的東西就更少了
##三.Web Workers 怎麼用
會說話的示例代碼如下:
//---主頁面
if (window.Worker) {
var worker = new Worker('worker.js');
var data = {a: 1, b: [1, 2, 3], c: 'string'};
worker.postMessage(data);
worker.onmessage = function(e) {
console.log('main thread received data');
console.log(e.data);
// 接到消息立即停止 worker,onerror 將不會觸發
// worker.terminate();
// terminate 之後收不到後續消息,但 post 不報錯
// worker.postMessage(1);
}
worker.onerror = function(err) {
console.log('main thread received err');
console.log(err.message);
// 阻止報錯
err.preventDefault();
}
}
//---end 主頁面
//---worker.js
// 可以引入其它依賴
// importScripts('lib.js');
// importScripts('a.js', 'b.js');
onmessage = function(e) {console.log(self); // 看看 global 變量身上有些什麼
var data = e.data;
console.log('worker received data');
console.log(data);
var res = data;
res.resolved = true;
postMessage(res);
setTimeout(function() {
throw new Error('error occurs');
// close,立即停止,相當於主線程中的 worker.terminate()
// close();
}, 100);
};
//---end worker.js
注意:
-
worker的 global context並不是window,而是self,self也提供一系列接口,包括self.JSON、self.Math、self.console等等,最直觀的區別是document對象沒了,但location、navigator還在 -
Worker 的原型是 DedicatedWorkerGlobalScope,與 SharedWGS 不同,Worker 是專用的,只有創建 Worker 的腳本才能訪問自己創建的 worker
-
worker裡不允許操作 DOM,但支持 WebSocket、IndexedDB、XHR 等等,各瀏覽器支持性有差異,具體請查看 Functions and classes available to Web Workers - Web APIs | MDN
注意:XHR 雖然可以用,但 XHR 對象的responseXML和channel屬性永遠返回null
-
主線程和 worker 線程收發消息方式一致(
postMessage發,onmessage/onerror收,數據從 MessageEvent 的data屬性取),線程之間傳遞的是值 copy,而不是共享引用 -
IE10 和 FF 支持在
worker裡 new Worker,只要這些 worker 和父頁面是同源的 -
支持性問題,建議做特性檢測
if (window.Worker) { // new Worker... } -
importScripts可以引入其它 js 文件,外部文件中的全局變量將被粘在self上,worker裡可以直接引用。importScripts是同步的,下載並執行完畢執行下一行
##四.Shared Worker
上面我們關注的 Worker 屬於 dedicated worker(專用 worker),只有創建 worker 對象的 context 有 worker 的訪問權。而 Shared Worker 支持跨 context 的 worker 共享,比如 window 與 iframe,iframe 與 iframe
由於它們具有共享的屬性,可以保持一個應用程序在不同窗口內的相同狀態,並且不同窗口的頁面通過同一共享 worker 腳本保持和報告狀態
用 localStorage 也可以實現狀態同步,zxx 前輩的例子:Page Visibility(網頁可見性) API 與登錄同步引導頁實例頁面。有興趣的話可以查看原文:Page Visibility(頁面可見性) API 介紹、微拓展
筆者能想到 Shared Worker 的唯一的適用場景可能就是實現應用狀態同步機制了,雖然 cookie、localStorage 都能夠存儲狀態,但 Shared Worker 能夠實現一套複雜的狀態控制機制。簡言之,cookie 和 localStorage 裡只能放數據,Shared Worker 裡除了能放數據,還能執行代碼
此外,Shared Worker 好像沒什麼用了,畢竟能new Worker已經很不錯了,錦上添花的東西作用不會讓人詫異
示例代碼如下:
//---主頁面
if (window.SharedWorker) {
var sworker = new SharedWorker('sharedworker.js');
// 1.onmessage 隱式調用 start
// sworker.port.onmessage = function(e) {
// console.log('main thread received data:');
// console.log(e.data);
// }
// 2.或者 addEventListener 再顯式調用 start
sworker.port.addEventListener('message', function(e) {
console.log('main thread received data:');
console.log(e.data);
});
sworker.port.start();
sworker.port.postMessage([1, 2, 3]);
}
//---end 主頁面
//---sharedworker.js
var count = 0;
onconnect = function(e) {
// 記錄連接數
count++;
var port = e.ports[0];
// 1.onmessage 隱式調用 start
port.onmessage = function(e) {
//!!! console.log 失效了
console.log('will not occur in console');
port.postMessage({receivedData: e.data, count: count});
//!!! 錯誤不會拋回給 sharedworker 創建者,靜默失敗
//!!! 如果 sharedworker 本身有語法錯誤,也會靜默失敗,而且在這之前的 postMessage 也將無效
// a*; // 製造一個語法錯誤
setTimeout(function() {
throw new Error('error occurs');
}, 100);
};
// 2.或者 addEventListener 再顯式調用 start
// port.addEventListener('message', function(e) {
// //!!! console.log 失效了
// console.log('will not occur in console');
// port.postMessage('sharedworker received data: ' + e.data);
// });
// port.start();
};
//---end sharedworker.js
在線 DEMO:testSharedWorker DEMO
測試方法:打開上面的鏈接,看 console 發現count為 1,不管怎麼刷新刷新都是 1,再開一個標籤頁打開該頁面,發現count變成 2 了,然後刷新刷新刷新,發現count變成 5 了,回到之前的標籤頁���刷新,count變成 6 了...
##五。線程安全問題
線程安全方面,不存在併發可能引發的各種問題,因為 Worker 線程的行為受到了限制(worker無法訪問非線程安全的組件和 DOM)
##六。其它 Worker
除了上面討論的 Web Workers,還有這些 Worker:
-
ServiceWorkers(類似於代理服務器,提供離線服務)
-
ChromeWorker(開發瀏覽器插件中可用的 Worker)
-
Audio Workers(支持音頻處理,暫無任何瀏覽器支持,2015/11/29)
###參考資料
暫無評論,快來發表你的看法吧