Blackbing Playground

Web Worker 經驗分享(一)

本文章同步刊載於IT邦幫忙

各位「網路工作者」大家好!

It's you! web worker

Web Worker 是由 W3C 以及 WHATWG 共同制定出來的API,讓 javascript 可以獨立在背景執行而不會影響到原來的HTML頁面,Web Worker 可以更有效率的執行前端程式。

聽起來很厲害,總是在跟效能搏鬥的前端工程師就會傻傻的想跳進這個坑(如我),但是由於沒有整體性的瞭解這個東西,只參考API想要改善任何效能上的問題就會有點心有餘而力不足,最後還是放棄,然後跟自己說:

「唉~算了啦~這麼不好用,等他成熟一點再說吧!」。

因此想跟大家分享一下我在使用 Web Worker上的一點點心得,以及在服用 Worker 之前應該要有的觀念。本文章希望從實作的角度來讓大家了解 Web Worker 到底可以做什麼,而不僅止於 API 的介紹。

前端效能瓶頸

隨著 Web Application 的演化,以往需要在 server 端處理的事情都慢慢搬到前端來處理,但 javascript 的問題是他是單執行緒,可以想像他就是瀑布式的執行每一個動作,雖然可以利用 event callback 來完成”非同步”的工作,但終究是在同一個瀑布上面流動。

例如報表資料,從 server 端取回資料之後,在前端進行排序,然後 render DOM,瀏覽器雖然越來越強,然而你的專案也越來越複雜,需求又千變萬化,如果你有遇過在處理資料時,操作 DOM 元素看起來像畫面當掉了一樣,那恭喜,你得到他了。除了你的邏輯有問題之外,單執行緒的問題就在此。

以往處理這種效能瓶頸時的解決方式,就是將你的動作拆開,利用 queue 以及 event callback 的概念,把動作拆成一步一步的執行,來避免 UI thread 負擔過重。但其實這就是在效能上做某種程度的妥協,而不是真正優化你的應用程式。

其實從前在設計桌面應用程式的時候,早就有多執行緒的概念了,為了避免在進行複雜運算時 UI blocking 的問題,就會開 multi thread 來避免影響UI的呈現。既然 web 已經 Application 化了,就免不了會開始遇到類似的問題。

我真的需要它嗎?

風遁‧螺旋手裏劍
[圖片引用來源]http://naruto.17dm.com/200912/23508_3.html

看看「漩渦鳴人」吧,只要學會了多重影分身術,可以利用影分身縮短修行時間,再難的招式開個多執行緒來幫忙就可以練得出來(關 Web Worker 屁事XD)。即使你沒聽過 Web Worker ,或是你沒遇過類似問題的,也可以藉著這個機會了解前端可以做到更多有趣的事情。才疏學淺的我在第一次接觸到 Web Worker 的時候,遇到一個很大的問題就是,網路上找到的資料,幾乎都是跟你說有這個 API,接著有個小範例,告訴你如何傳遞訊息,如何傳遞回來,接下來就是API的Spec。但實作上的時候卻遇到一堆問題,難道我只會要他傳遞文字資料嗎?(╯‵□′)╯╧╧

因此在實作之前,我希望可以掌握到 Web Worker 到底能做到什麼?不能做到什麼,再從而瞭解遇到什麼情況適合交給 Web Worker來做。若一知半解在實作上就會遇到一堆問題。那麼,就讓我們一起來好好深入地研究吧!(握拳!)

CAN I USE?

Web Worker 其實不算新了,早在 2009 年 Firefox3.5 就已經出現 Worker 的實作了,只是各家瀏覽器的支援程度不一致,而且 IE 在兩三年前還在地球上稱王,因此相關的應用並不算太多。Can I use Web Worker? 到目前為止,如果你的應用程式可以拋棄 IE8, IE9 的包袱的話,那就大膽的用吧!Firefox, Chrome, Safari, Opera, IE10 甚至 iOS Safari,Android Chrome 都支援。

can i use web worker?
[圖片引用來源]:http://caniuse.com/#search=worker

讓我們開始吧

要起始一個 Worker 很簡單,只要呼叫 new 一個 Worker 即可,剩下的工作就交給你要執行的 script 了。

起始Worker

1
var worker = new Worker('worker.js');

Worker 接的字串參數就是 script 的路徑,當你 new 了一個 Worker 之後,就會根據這個路徑,非同步的下載這一隻 script ,接著你就可以開始跟他溝通了。

1
2
3
4
worker.postMessage(something);
worker.addEventListener('message', function(e) {
console.log('Worker said: ', e.data);
}, false);
  1. postMessage 是最主要溝通的 function,可以丟任何形態的參數過去。
  2. 接著定義當收到 worker 傳回來的資料時的動作,這裡就是直接印到 console 出來看。
  3. e.data 指的就是postMessage傳遞的資料。

Worker script裡頭要做的事情

而在 worker.js 裡頭,就要定義好收到資料時的 function 。來作相對應的處理。

1
2
3
4
//worker.js
self.addEventListener('message', function(e) {
self.postMessage(e.data);
}, false);

在 Worker script 裡頭,在 worker.js 裡頭 self 就代表 Worker 本身(在html裡頭是指向 window Object),上面的例子是定義接收到資料時就直接將資料用 postMessage 回傳回去。

OK,這就是一個很基本的流程。

Web Worker的限制

看起來不難,是吧?不過要小心,Worker 可沒這麼好使喚,有些東西你要丟給他他可是會吐掉的!我們先來理解 Web Worker 的限制吧!

由於安全性的考量,Web Worker 無法存取下列資源。

  1. window
  2. DOM
  3. document
  4. parent

主頁面的 html 與 Worker 的資源是不能共享的,也就是說你無法在 Worker 裡頭直接存取原頁面的變數,他們之間唯一可以傳遞資料的方式是就是 postMessage 方法。那麼,如果你嘗試著要將這些物件傳給 Worker 時,例如:

1
worker.postMessage(window);

它就會直接吐 Error 給你。

try to pass window

也因此,你也不能在 Worker 裡頭做任何有關 DOM 元素的操作,比方說 createElement(‘canvas’) 之類的事情。

What the hell do I use web worker?
[圖片引用來源]:http://ajaxian.com/archives/an-implausibly-illustrated-introduction-to-html5-web-workers

ㄜ,什麼都不能用那 Web Worker 到底可以做啥?

Web Worker可以使用

好吧,換個方式問好了,那麼 Web Worker 可以「用」什麼?

  1. navigator
  2. location(read-only)
  3. XMLHttpRequest
  4. setTimeout/setInterval
  5. Basic Javascript data Structure and Function(Math, Date, Array, etc.)

Image comparing the execution environment of a web page to that of a web worker with the worker environment does not include window, the document, elements, or attributes
[圖片引用來源]:http://blogs.msdn.com/b/ie/archive/2011/07/01/web-workers-in-ie10-background-javascript-makes-web-apps-faster.aspx

上面的示意圖很清楚的列出Web Worker可以存取的資源的差異,簡單地說,Worker就只能運行基本的javascript,以及XMLHttpRequest,所以如果你的Application需要大量的跟server端發request的話,那將request的部分移到Worker做就會是個最基礎的應用。

Functions avavilable to Workers 有詳細列出在Worker裡頭可以使用的function,很有用。

複雜的計算?

例如費伯納西數列 (Fibonacci),質數 (prime number)等複雜的計算,大量陣列的排序,就會非常吃運算效能。

使用情境

「但是實際應用上,我們不會算費伯納西數列 (Fibonacci) ,我們不會算最大的質數是多少,你可以說點人話嗎?」

嗯,的確,Front-end 要處理的問題幾乎都是頁面的 render ,一時之間可能很難想到有什麼情境可以用到。在想使用情境之前,對我來說,最簡單的解釋就是把 Worker 當成是「Client-server」的概念,我們來復習一下 主從式架構(Client-server) 的特色。

主從式架構

定義:主從式架構 (Client–server model) 或客戶端-服務器(Client/Server)結構簡稱 C/S 結構,是一種網絡架構,它把客戶端 (Client) (通常是一個採用圖形用戶界面的程序)與服務器 (Server) 區分開來。每一個客戶端軟件的實例都可以向一個服務器或應用程序服務器發出請求。

  • Server Side
    • 被動
    • 等待Client Side的要求
    • 處理要求並傳回結果
  • Client Side
    • 主動
    • 發送要求
    • 等待直到收到回應

[資料來源]:wiki: 主從式架構

(大驚變頻空調!!) Worker 的限制和用法不就跟這一樣?也就是說還可以從前端直接 new Instance 起來,要幾台有幾台!?這樣一來,就把它想成是「前端的後端」,所以 Server 端可以做到的情境,就是 Worker 的使用情境。

##小結

若要思考 Web Worker 的使用情境,其實只要將 Web Worker 當作是 Client-Server 的架構來思考。事實上,我們已經在做類似的事情了。思考一下過去的Web Page,後端做複雜的運算,例如資料排序,DB查詢等等,前端很簡單就是做表格畫面的繪製。但隨著Web Application的發展,前端已不只是在渲染畫面,需要進行更多複雜的運算與頁面做交互作用。雖然瀏覽器很盡力的將事情完成,但我們其實可以將頁面的繪製與複雜運算再分開,做更詳細的工作劃分,讓頁面繪製可以更加流暢,藉此提高效能,改善使用者體驗。

Web Worker 經驗分享(二)