讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議186:推薦100 ms用戶體驗 >

建議186:推薦100 ms用戶體驗

確保網頁應用程序的響應速度是一個重要的性能關注點。總的來說,大多數瀏覽器有一個單獨的處理進程,它由兩個任務所共享:JavaScript任務和用戶界面更新任務。每一刻只有其中的一個操作得以執行,也就是說,當JavaScript任務運行時用戶界面不能對輸入產生反應,反之亦然。或者說,當JavaScript任務運行時,用戶界面就被鎖定了。管理好JavaScript運行時間對網頁應用的性能很重要。

JavaScript和UI更新共享的進程通常稱做瀏覽器UI線程。UI線程圍繞著一個簡單的隊列系統工作,任務被保存到隊列中。一旦進程空閒,隊列中的下一個任務將被檢索和運行。這些任務不是運行JavaScript代碼,就是執行UI更新,包括重繪和重排版。此進程中最令人感興趣的部分是每次輸入均導致一個或多個任務被加入隊列。

在下面示例中頁面包含一個按鈕,按下該按鈕,屏幕上顯示一個消息。


<html>

<head>

<title></title>

</head>

<body>

<button onclick=\"handleClick\">

Click Me

</button>

<script type=\"text/javascript\">

function handleClick{

var p=document.createElement(\"p\");

p.innerHTML=\"Clicked!\";

document.body.appendChild(p);

}

</script>

</body>

</html>


當頁面中的按鈕被單擊時,將觸發UI線程創建兩個任務並將它們添加到隊列中。第一個任務是按鈕的UI更新,需要改變按鈕外觀以指示它被按下了;第二個任務是JavaScript運行任務,包含handleClick的代碼,被運行的唯一代碼就是這個方法和所有被它調用的方法。假設UI線程空閒,第一個任務被檢查並運行以更新按鈕外觀,然後JavaScript任務被檢查和運行。在運行過程中,handleClick創建了一個新的<p>元素,並追加在<body>元素上,其效果是引發另一次UI改變。也就是說,在JavaScript運行過程中,一個新的UI更新任務被添加到隊列中,當JavaScript運行完之後,UI還會再更新一次。

當所有UI線程任務執行之後,進程進入空閒狀態,並等待更多任務被添加到隊列中。空閒狀態是理想的,因為所有用戶操作都會立刻引發一次UI更新。如果用戶企圖在任務運行時與頁面交互,不僅沒有即時的UI更新,而且不會有新的UI更新任務被創建和加入隊列。事實上,大多數瀏覽器在JavaScript運行時停止UI線程隊列中的任務,也就是說,JavaScript任務必須盡快結束,以免對用戶體驗造成不良影響。

瀏覽器在JavaScript運行時間上採取了限制。這是一個有必要的限制,確保惡意代碼編寫者無法通過無盡的密集操作鎖定用戶瀏覽器或計算機。此類限制有兩個:棧尺寸限制和長時間運行腳本限制。長時間運行腳本限制有時被稱做長時間運行腳本定時器或失控腳本定時器,其基本思想是瀏覽器記錄一個腳本的運行時間,一旦到達一定限度時就終止它。當到達此限制時,瀏覽器會向用戶顯示一個對話框。

有兩種方法測量腳本的運行時間。第一個方法是統計自腳本開始運行以來執行過多少語句。此方法意味著腳本在不同的機器上可能會運行不同的時間長度,可用內存和CPU速度可以影響一條獨立語句運行所花費的時間。第二種方法是統計腳本運行的總時間。在特定時間內可運行的腳本數量也因用戶機器性能差異而不同,但腳本總是停在固定的時間上。毫不奇怪,每個瀏覽器在對長時間運行腳本檢查方法上略有不同。

❑IE設置默認限制為500萬條語句,此限制存放在Windows註冊表中,叫做HKEY_CURRENT_USERSoftwareMicrosoftInternetExplorerStylesMaxScriptStatements。

❑Firefox默認限制為10 s,此限制存放在瀏覽器的配置設置中(在地址欄中輸入about:config),鍵名為dom.max_script_run_time。

❑Safari默認限制為5 s,此設置不能改變,但可以關閉,通過啟動Develop菜單並選擇「禁止失控JavaScript定時器」來關閉此定時限制。

❑Chrome沒有獨立的長時間運行腳本限制,代之以依賴它的通用崩潰檢測系統來處理此類實例。

❑Opera沒有長運行腳本限制,將繼續運行JavaScript代碼直至完成。由於Opera的結構特點,當腳本運行結束時並不會導致系統不穩定。

當瀏覽器的長時間運行腳本限制被觸發時,不管頁面上的任何其他錯誤處理代碼,都會有一個對話框顯示給用戶。這是一個主要的可用性問題,因為大多數互聯網用戶並不精通技術,會被錯誤信息所迷惑,不知道應該選擇哪個選項(停止腳本或允許它繼續運行)。

如果腳本在瀏覽器上觸發了此對話框,意味著腳本用太長的時間來完成任務。它還表明用戶瀏覽器在JavaScript代碼繼續運行狀態下無法響應輸入。從開發者的觀點來看,沒有辦法改變長運行腳本對話框的外觀,不能檢測到它,因此不能用它來提示可能出現的問題。顯然,長運行腳本最好的處理辦法是避免它們。

瀏覽器允許腳本繼續運行直至某個固定的時間,這並不意味著可以這樣做。事實上,JavaScript代碼持續運行的總時間應當遠小於瀏覽器實施的限制,以創建良好的用戶體驗。

如果幾秒鐘對JavaScript運行來說太長了,那麼多長時間是適當的呢?事實證明,即使一秒鐘對腳本運行來說也太長了。一個單一的JavaScript操作應當使用的總時間(最大)是100 ms。如果某接口在100 ms內響應用戶輸入,那麼用戶認為自己是在直接操作用戶界面中的對象。響應時間超過100 ms意味著用戶認為與接口斷開了。由於UI在JavaScript運行時無法更新,因此運行時間大於100 ms就會使用戶感受不到對接口的控制。

更複雜的是有些瀏覽器在JavaScript運行時不將UI更新放入隊列。例如,在某些JavaScript代碼運行時單擊按鈕,瀏覽器可能不會將重繪按鈕的UI更新任務放入隊列,也不會將由這個按鈕啟動的JavaScript任務放入隊列。其結果是一個無響應的UI,表現為掛起或凍結。

每種瀏覽器的行為大致相同。當腳本執行時,UI不隨用戶交互而更新。此時JavaScript任務作為用戶交互的結果被創建並放入隊列,然後當原始JavaScript任務完成時隊列中的任務被執行。用戶交互導致的UI更新被自動跳過,因為優先考慮的是頁面上的動態部分。因此,當一個腳本運行時單擊一個按鈕,將看不到它被按下的樣子,即使它的onclick句柄被執行了。

儘管瀏覽器嘗試在這些情況下做一些符合邏輯的事情,所有這些行為還是會導致一個間斷的用戶體驗。因此,最好的方法是,通過限制任何JavaScript任務在100 ms或更少時間內完成,避免此類情況出現。這種測量應當在支持得最慢的瀏覽器上執行。