javascript
《高性能JavaScript》第六章 快速响应的用户界面
6.1 瀏覽器UI線程
瀏覽器UI線程:用于執行JavaScript和更新用戶界面的進程。UI線程的工作基于一個簡單的隊列系統,任務會被保存到隊列中直到進程空閑。
-------------------------------------------------------------------- 注:如果你對python感興趣,我這有個學習Python基地,里面有很多學習資料,感興趣的+Q群:895817687 --------------------------------------------------------------------<html><head><title>Browser UI Thread Example</title></head><body><button onclick="handleClick()">Click Me</button><script type="text/javascript">function handleClick(){var div = document.createElement("div");div.innerHTML = "Clicked!";document.body.appendChild(div);}</script></body></html>6.1.1 瀏覽器限制
瀏覽器限制了JavaScript任務的運行時間,它確保某些惡意代碼不能通過永不停止的密集操作鎖住用戶的瀏覽器或計算機。此類限制分為兩種:調用棧大小限制(第四章討論過)和長時間運行腳本限制。
對于不同瀏覽器限制也不一樣:
IE:默認500萬條語句;
Firefox:默認限制10秒;
Safari:默認限制5秒;
Chrome:沒有單獨限制,替代做法是依賴其通用崩潰檢測系統來處理;
Opera:沒有限制,鑒于Opera架構,運行結束時不會導致系統不穩定。
6.1.2 多久才算太久
單個JavaScript操作花費的總時間不應該超過100毫秒。這個數字源自Robert Miller在1968年的研究。如果頁面響應超過100毫秒,用戶會感到自已與界面失去聯系。因此,限制所有的JavaScript任務在100毫秒或更短的時間內完成,才能給用戶一個連續的體驗。
6.2 使用定時器讓出時間片段
盡管盡了最大努力,有時也難免出現復雜的JavaScript任務不能在100毫秒內完成。這時候,最理想的做法是讓出UI線程的控制權,使得UI可以更新。
###6.2.1 定時器基礎
在JavaScript中可以使用setTimeout()和setInterval()創建定時器。定時器和UI線程的交互方式有助于把運行耗時較長的腳本拆分為較短的片段。
注意:定時器傳入的時間參數表示任務被添加到UI線程的時間,而不是一定會在這段時間后執行,它會在隊列中等待其他前面所有任務執行完畢才會執行。
var button = document.getElementById("my-button");button.onclick = function(){oneMethod();setTimeout(function(){document.getElementById("notice").style.color = "red";}, 250);};注意:定時器代碼只有在創建它的函數執行完成之后,才有可能被執行。
var button = document.getElementById("my-button");button.onclick = function(){oneMethod();setTimeout(function(){document.getElementById("notice").style.color = "red";}, 50); anotherMethod();};6.2.2 定時器的精度
JavaScript定時器的延遲通常不太精準,相差大約幾毫秒。正因為如此,定時器不能用于測量實際時間。設置定時器延遲小于15毫秒會導致IE鎖定,所以延遲的最小值建議設為25毫秒,以確保至少有15毫秒延遲。
6.2.3 使用定時器處理數組
在之前討論的循環優化技術使用后,還不能滿足性能要求的時候,下一步優化就是選用定時器。它的基本方法是把循環工作分解到一系列定時器中。
是否可以用定時器取代循環的兩個決定性因素:
1:處理過程是否必須同步?
2:數據是否必須按順序處理?
如果這兩個答案都是:否,那么代碼將適用于定時器分解任務。
注意:使用定時器的副作用是處理數組的總時長增加了,但為了避免鎖定瀏覽器給用戶帶來不好的體驗,這種取舍是必要的。
6.2.4 分割任務
我們通常會把一個復雜的任務分解成一系列子任務。
function saveDocument(id){// 保存文檔openDocument(id)writeText(id);closeDocument(id);// 將成功信息更新至頁面updateUI(id);} function multistep(steps, args, callback){var tasks = steps.concat(); // 克隆數組setTimeout(function(){// 執行下一個任務var task = tasks.shift();task.apply(null, args || []);// 檢查是否還有其他任務 if (tasks.length > 0){setTimeout(arguments.callee, 25);} else {callback();}}, 25);} // 使用function saveDocument(id){var tasks = [openDocument, writeText, closeDocument, updateUI];multistep(tasks, [id], function(){alert("Save completed!");});}6.2.5 記錄代碼運行時間
有時每次只執行一個任務的效率不高。如果每次定時器只處理一項,且每項之間產生25毫秒的延遲,這對性能來說是個浪費。如果每個定時器能批處理多個任務,那么效率就上來了。
還記得前面討論過JavaScript連續運行100毫秒,不會影響用戶體驗。建議把這個數字減半,不讓JavaScript代碼持續運行50毫秒,這樣確保代碼永遠不會影響用戶體驗。
該函數增加了一個do-while循環,保證一個定時器里任務總耗時不超過50毫秒,既增加了效率有不影響用戶體驗。
6.2.6 定時器與性能
定時器會讓JavaScript的代碼整體性能發生翻天覆地的變化,但過度使用也會對性能造成負面影響。保證同一時間只有一個定時器存在,才能避免定時器導致的性能問題。
6.3 Web Workers
自JavaScript誕生以來,還沒有辦法在瀏覽器UI線程外運行代碼。Web Workers改變了這一狀況,它引入了一個接口,能使代碼運行且不占用瀏覽器UI線程時間。
6.3.1 Worker運行環境
由于Web Workers沒有綁定UI線程,意味著它們不能訪問瀏覽器的許多資源。Web Workers從外部線程中修改DOM會導致用戶界面出現錯誤,但是每個Web Worker都有自己的全局運行環境:
1:一個navigator對象,只包含四個屬性:appName,appVersion,userAgent,和platform;
2:一個location對象(和window里的一樣,只是所有屬性都是只讀的);
3:一個self對象指向全局worker對象;
4:一個importScripts()方法,使工人線程可以加載外部 JavaScript 文件;
5:所有ECMAScript對象,諸如Object,Array,Data,等等;
6:XMLHttpRequest構造器;
7:setTimeout()和setInterval()方法;
8:close()方法可立即Worker運行。
由于Web Workers有著不同的全局運行環境,因此無法從JavaScript代碼中創建。需要創建一個完全獨立的JavaScript文件,包含了需要在Worker中運行的代碼。然后引入這個文件:
var worker = new Worker(“code.js”);
此代碼一旦執行,將為這個文件創建一個新的線程和一個新的Worker運行環境。該文件會被異步下載,直到文件下載并執行完成后才會啟動Worker。
6.3.2 與Worker通信
Worker與網頁代碼通過事件接口進行通信。網頁代碼通過postMessage()方法給Worker傳遞數據;Worker有一個onmessage事件處理器接收信息,并可以用自己的postMessage()方法把數據傳回網頁代碼。
// 網頁代碼var worker = new Worker("code.js");worker.onmessage = function(event){alert(event.data);};worker.postMessage("Nicholas");// code.js內部代碼self.onmessage = function(event){self.postMessage("Hello, " + event.data + "!");};注意:消息系統是網頁和Worker通信的唯一途徑。
6.3.3 加載外部文件
Worker通過importScripts()方法加載外部JavaScript文件,該方法接收一個或多個JavaScript文件URL作為參數。
// code.js內部代碼importScripts("file1.js", "file2.js");self.onmessage = function(event){self.postMessage("Hello, " + event.data + "!");};6.3.4 實際應用
Web Workers適用于哪些處理純數據,或者與瀏覽器UI無關的長時間運行腳本。比如:
1:編碼/解碼大字符串;
2:復雜數學運算(包括圖像或視頻處理);
3:大數組排序。
性能優化:任何超過100毫秒的處理過程,都應考慮Worker方案而不是定時器方案。前提是瀏覽器支持Web Workers。
總結
以上是生活随笔為你收集整理的《高性能JavaScript》第六章 快速响应的用户界面的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《高性能JavaScript》第五章 字
- 下一篇: 《高性能JavaScript》第七章 A