今天要來介紹的是 Web Works,其實這個技術已經很久了,不過知道歸知道,瀏覽器不支援也沒法用
但隨著舊版本IE的沒落,感覺應該是可以開心使用了
先來看一下支援表
首先望向進步的反指標 IE,沒有看到 IE10......不,感覺是直接當作從來沒有 IE10的樣子
找了半天,還好展開全部發現 IE10是有支援的,反而Android Browser 要非常小心
雖然我的Android手機現在都6.0了,但應該還有一些低階手機還在 4.X
不過只要IE10有支援就可以開始學了,再來稍微介紹一下它的由來與用途 (底下都是做完功課後的結論)
Web Works 是一個W3C制定的規範,起源於2008 年W3C制定的第一個HTML5草案中
有興趣可以參考W3C中的 workers:https://www.w3.org/TR/workers/
在沒使用 Web Works 之前,我們使用的瀏覽器其實一直都是單一執行緒的 (叫做 UI thread)
也就是縮,你在瀏覽器上 run 的指令碼都會被限定在單一 UI thread 中執行
因此只要執行了一個需要大量運算的工作,整個頁面就會因為這個工作而停住了
當然有其他解決方式可以稍微舒緩一下這個打結的狀況,但也很明顯的點出了單一執行緒的缺點
而且近來前端技術火熱的關係,有些之前在後端做的運算都被移到前端來了
雖然大家電腦配備也變強了,但畢竟瀏覽器還是單一執行緒
因此這個時候使用 Web Works 就是個好選擇,我們可以將複雜的運算交給 Web Works 去處理
就不會因為單一執行緒處理時間過長而影響到使用者的操作體驗
下面取用一下 MDN 中的說明:
https://developer.mozilla.org/zh-TW/docs/Web/Guide/Performance/Using_web_workers#Web_Workers_API
根據 MDN 的說明 worker 又區分以下幾種
1. Dedicated worker (專有 worker)
是一般 worker,只能被產生它的檔案存取
2. Shared worker (共享 worker)
則能夠被不同檔案存取
3. ServiceWorkers
基本上如同介於 web app 和瀏覽器以及網路之間的代理伺服器 (proxy server),這類 worker 重點在實現離線服務,service worker 會攔截網路請求,然後依據網路連線和資源狀態做出反應,他們可以存取推播和背景同步 APIs。
4. Chrome Workers
是 Firefox 唯一的 worker 類型,他們可以用在開發 add-ons,或是想要使用 js-ctypes。詳情請見 ChromeWorker。
5. Audio Workers
主要用於音效處理部分。
此篇只會練習 Dedicated worker (專有 worker) 與 hared worker (共享 worker),其他感覺我用不太到
以下先使用 Dedicated worker 的簡單範例,一步一步把自己(我) 當白痴來做練習
Dedicated worker (專有 worker)
Step1:
首先建立一個 js,檔名隨便,這邊取名叫 work.js,目的是要建立可在另一個執行緒中 run 的 js
work.js:
Step2:
再來建立一個要在主執行緒 run 的頁面,這邊取名叫 main.html,裡面先放 js 就好
main.html:
Step3 (寫完了):
這樣就完成了一個 web workers 的最簡單範例,按F12打開瀏覽器 console 可以看到以下訊息
首先先來解說一下上面的 code
main.html:
1. 在 main.html 之中,我們用 if (window.Worker) { ..... } 來判斷瀏覽器是否支援 web workers
2. 透過 worker 建構子就可以產生 worker 物件,ex:var worker = new Worker('worker.js');
透過 new 建構時傳入的字串就是 worker.js 的路徑,他會運行在不同於 window 的執行緒環境
3. 使用 worker 的 onmessage 的方法,接收另一個執行緒 worker.js 回傳的訊息
(要注意是全小寫! onmessage)
4. 從主要執行緒中使用 postMessage() 方法,來傳送資料到 worker 執行緒
work.js:
1. 使用 onmessage = function (e) { ..... } 來接收主 UI thread 用 postMessage() 過來的資料
2. 事件物件 e 中的 data 屬性會存在有傳送過來的資料,此例而言是直接 console.log() 輸出
上面例子用的很簡單,基本上就是建立 workers 物件
然後在 thread 中傳遞訊息都透過 postMessage(),接收訊息使用 onmessage
但是 worker 在使用上有些限制,請看下面
Dedicated worker使用限制:
由於 worker 中的 javascript 運行在不同於 window 的主執行緒裡面
所以要在 worker 中存取全域物件必須要使用 self 才行,如果用 window 就會有錯誤
基於安全性的考量,除了上面講的 window 不能用之外,worker 也沒辦法存取以下的資源
1. DOM
2. document
3. parent
主網頁跟 worker 之間的溝通只能透過 postMessage() 方法,且不能偷傳上面說的物件進去
也不能傳 function 進去,不然會出現錯誤訊息,也不能使用 alert、confirm 之類的函數
但可以傳 window 下的 setTimeout 跟 setInterval
其實有點亂,還是只要記可以傳什麼就好了
MDN上列出可以使用的有以下這些:
a. Navigator
c. Array, Date, Math, 與 String
d. Window.requestAnimationFrame, WindowTimers.setTimeout, 與 WindowTimers.setInterval
Shared worker (共享 worker)
看完了專有 worker 的用法,接下來看一下 Shared worker 的寫法
但是在此之前要先來看一下關於 shared worker 比較致命的地方
Can I Use:http://caniuse.com/#feat=sharedworkers
IE 看起來是不支援啊!!! 行動裝置更慘幾乎死一片
得知這慘狀之後當下有點不太想學了,但是看了一下 code 覺得不太難,稍微了解一下也好
shared worker 最強的地方就是可以跨網頁,縱使跨越不同 window、iframe 或 worker
也就是說,他可以同時服務多個網頁,底下來看看怎麼寫
Step 1:
首先建立一個 js,這邊取名叫shared-worker.js,目的是要建立可在另一個執行緒中 run 的 js
shared-worker.js:
Step 2:
再來建立一個要在主執行緒 run 的頁面,這邊取名叫 main2.html,裡面先放 js 就好
main2.html
Step 3:
F12 打開 console 之後就會看到下面的訊息
然後同樣的頁面,再chrome上再開一個新的 tab,你會發現數字被 +1 了
這威力根本就像 server 中的 Application (以 .net 來說),使用到的頁面都共用同一個 worker
但因為支援度不高,所以快速的了解一下就好了
shared-worker.js:
1. 用 onconnect = function (e) { .... } 建立監聽連線的 onconnect 事件,就可取得 onconnect 事件物件
然後透過該物件就可以取得 port 物件,port 物件指的就是 MessagePort,代表著連線的接口
port 有底下三種方法可以使用
a. onmessage (訊息接收)
b. postMessage (就是發送訊息,要跟worker 溝通都只能用他)
c. start (開啟連線)
d. close (關閉連線)
此例中我們將收到的 port 物件用 connections.push(port) 寫到陣列,同時使用 start() 開啟 port 連線
2. 建立一個 onmessage 事件來接收每個 port 傳來的 message,一樣透過事件物件 .data 來取值
每接收一次 counter 變數 就 + 1,同時對 connections 陣列中的所有 port 用 postMessage 發送訊息
發送的內容是如下的物件
再來看一下 main2.html
1. 在 main2.html 之中,我們用 if (window.SharedWorker) { ... } 來判斷瀏覽器是否支援 SharedWorker
2. 透過 port 物件的 onmessage 方法接收訊息,其實 dedicated worker 也是需要 port
只不過一切是在背景後自動完成,所以使用上也比較簡單
3. onmessage 裡一樣用事件物件 e.data 去取值
4. 在主 UI thread 中使用 postMessage 發送訊息到 worker
因為支援度太低,故介紹到此結束!
Works 的錯誤處理
MDN 官網的說明:
使用方法是像這樣
三個屬性指的分別是
message:供人閱讀的錯誤訊息
filename:錯誤發生所在的檔案名稱
lineno:錯誤發生所在的行數
結束 worker
在主執行緒裡呼叫 terminate 就可結束 worker,ex:worker.terminate();
但不論 worker 正在執行的運算完成與否,一但呼叫後 worker 便會立刻被終止
而在 worker 執行緒裡,worker 可以呼叫自己的 close 方法來結束 :
ex:self.close();
SubWorker:
目前只有 firefox 比較完整支援 SubWorker,所以不想學
參考資料:
MDN:https://developer.mozilla.org/zh-TW/docs/Web/Guide/Performance/Using_web_workers#Web_Workers_API
Worker 所有的方法以及可使用的 API:Functions and classes available to Web Workers
W3C:https://www.w3.org/TR/2015/WD-workers-20150924/ (目前最後一版是 2015)
Web Worker 經驗分享(一):http://ithelp.ithome.com.tw/articles/10118851
Intro to Web Workers:https://zapier.com/engineering/intro-to-web-workers/
留言列表