Non-blocking Web UI:不會卡的UI
之前做Project時碰到很嚴重的Blocking問題,剛好看到Spine.js作者的這篇
Asynchronous UIs - the future of web user interfaces
這篇點出了一個pattern: 我們設計的UI很多時都是”click-and-wait” (點擊與等待),而User是討厭等待的生物…所以AUI應此而生。
而AUI的基本,是Non-blocking -
- 即使有大量JS Event或運算,網頁仍然有能力回應User的UI Event (mouse click之類)。
- 沒有
該死的會鎖死整頁的progress bar/spinner但同時仍然有progress可見,User在等的時候仍然有事可做又不會誤會”thing is done”而關了網頁
2基本上是設計,然而1是技術-因為Browser對網頁JS的處理形式一不留神就會出現Blocking (輕則click沒反應,重則會出現沙漏/彩球):
- JS processing是Single-thread (和Java早期GUI只有一條main-thread有點像) [0]
- 一般而言JS運算很快^,除以下幾個情況:
- DOM Operation (document.*, element.*) [1]
(所以jQuery的animation是慢到不行的東西,動態HTML元素生成及附加也是) eval, with, for-in[2]- JSON Parsing & Stringfity [1] (webkit-only)
- XMLHttpRequest [1]
- jQuery的Event處理 (jQuery 1.7的
.on() & .off()就是為了修理這件事…) [3] - 特定function組合 - e.g.
setTimeout()/setInterval()內有requestAnimationFrame()[4]
^2 jQuery的可是結結實實的碰過釘啊… - DOM Operation (document.*, element.*) [1]
- 以上種種情況一旦沒控制好皆可能會出現Blocking,而解決Blocking不外乎是yielding & threading
- threading: JS只有兩種情況下可以開thread,Web Worker或是XHR。不過前者不能做DOM Operation而後者會開Network connection會增加Browser負擔(所以慢),應用範圍有限。
- yielding: 簡單說就是強行留cpu time給browser處理UI Event。Yielding基本上可以透過setInterval、setTimeout和setImmediate達成,詳見
用 setInterval 防止瀏覽器卡死
Script yielding with setImmediate
*要說yielding會有甚麼問題,主要問題在電量和Timer resolution之分歧上-各個browser+OS對”0 delay”的定義可是毫不一致,4ms ~ 16ms都有。
如果不做threading/yielding又要UI不卡,一要CPU夠力,二要GPU對某些功能有硬體加速** (iOS/webkit上的CSS3硬體加速,各大browser對canvas/webGL以至SVG的硬體加速等),然而這樣做一到mobile可以死的非常慘-是以慎重的處理程式內的Event及DOM操作-event handler有沒有call stopPropagation()、對吃cpu的後續動作加上yielding是很重要。
…要是早一點知道Spine.js也許就不會那麼血淚了 :(
(又,Mobile Browser是面很好的照妖鏡或code breaker…)
**有硬體加速和沒有的分別在?用有硬解的機器(iPad/iPhone)和沒硬解的機器(netBook)看720p的mp4片子就知道了…cpu用量(=機器表面溫度)差超多的。
Off-Topic: 為什麼JS Processing會是Single thread?
因為曾經有個叫Safari 2.0的傢伙試行multi-thread (全部script node multi-thread同時執行),結果死很大,因此沒有一家敢用multi-thread了。(也可能是Spec在有人如此胡來後改了也說不定)