Javascript之UI線程與效能最佳化

來源:互聯網
上載者:User

    在瀏覽器中,Javascript執行與UI更新是發生在同一個進程(瀏覽器UI線程)中的。UI線程的工作基於一個簡單的隊列系統,任務會被儲存到隊列 中直到進程空閑時被提取出來執行。所以Javascript的執行會阻塞UI更新;反之,UI更新也會阻塞Javascript的執行。給使用者的表現就是 瀏覽器在工作時短暫或長時間失去反應,使用者的操作不能及時得到響應。而UI線程的阻塞很多時候是由於我們要在代碼裡進行長時間的指令碼運算,超過了瀏覽器限 制,導致瀏覽器失去響應,凍結使用者介面。

    所以,編碼時對於耗時較長的運算我們不得不考慮UI線程的問題,《High Performance JavaScript》建議javascript操作耗時不應該超過100毫秒,那麼這個100毫秒是如何得出來的呢?

    我們來看下2個相關的研究:

1.1、Definitions of Response Time

    引用:Robert B. Miller:《Response time in man-computer conversational》

    不同類型的響應以及響應延時,適用於不同的行為水平。回應時間是基於對心理依據的估計而定義的。Robert Miller在其研究中定義了17種不同類型的回應時間,有興趣的同學可以細讀。這裡只列舉2條比較重要的:

    Topic 1. Response to control activation (開關控制響應)

    The click of the typewriter key, or the change in control force after moving a switch past a detent position are examples. They indicate responsiveness of the terminal as an object. This response should be immediate and perceived as a part of the mechanical action induced by the operator. Time delay: No more than 0.1 second.
    開關類操作中,終端相應能力對於操作者應該是直接的並可感知的。這類型的延時不應大於0.1秒。

    the delay between depressing the key and the visual feedback should be no more than 0.1 to 0.2 seconds.
    按鍵與可視反饋延時不應大於0.1~0.2秒。

    Note that this delay in feedback may be far too slow for skilled keyboard users.
    研究中同時提到,以上數值對於那些鍵盤高手還是很慢的。他們的預期值比一般人要高,所以以上數值只是一個普遍適用的數值。

    Topic 13. Graphic response from light pen (游標圖形響應)

    Where the lines are drawn with deliberation by the user—relatively slowly as compared with slashing sketch strokes—a delay of up to 0.1 second seems to be acceptable. There must not be variability perceived by the user in this delay.
    使用者的游標輸入延時,0.1秒是可被接受的。延時期間使用者是動態可感知的。

    The response delay in the image following the light pen may be as much as one second because the user is not tracing a line but positioning an image that, for him, is completed when his stylus touches the destination for the image.
    在構圖中,類似的延時1秒是可以被接受的。

1.2、Response Times: The 3 Important Limits

    引用:Jakob Nielsen:《Response Times: The 3 Important Limits》

0.1 second is about the limit for having the user feel that the system is reacting instantaneously, meaning that no special feedback is necessary except to display the result.
1.0 second is about the limit for the user's flow of thought to stay uninterrupted, even though the user will notice the delay. Normally, no special feedback is necessary during delays of more than 0.1 but less than 1.0 second, but the user does lose the feeling of operating directly on the data.
10 seconds is about the limit for keeping the user's attention focused on the dialogue. For longer delays, users will want to perform other tasks while waiting for the computer to finish, so they should be given feedback indicating when the computer expects to be done. Feedback during the delay is especially important if the response time is likely to be highly variable, since users will then not know what to expect.

    回應時間:3條重要的限制

0.1 senond : 0.1S,限制為達到使用者感知系統瞬時響應,意味著無需特別反饋。
1.0 senond : 1.0S,限制為使用者思路不被打斷,即使使用者感覺到延時。通常情況下,在0.1S~1.0S間無需特別的反饋,但使用者此次操作確實感覺到了興趣上的損失。
10 seconds : 10S,限制為使用者在對話中的注意力極限;更長的延時會導致使用者切換到其它任務以等待電腦完成當前任務。所以,在電腦完成當前任務前需要給予適當的反 饋。如果回應時間是高度可變的話,延時期間的反饋是尤其重要的,因為使用者不知道接下來將會發生什麼。
    實際工作中,很多任務都不可能在100ms內完成。很多複雜的運算(例如遞迴與迭代),都要佔用UI線程,使用者的其它操作沒有得到及時的響應,瀏覽器介面 被凍結、棧溢出等情況時有發生。大部分瀏覽器在遇到長時間指令碼運算時都會給予使用者提示是否終止操作,對於很多使用者來說,選擇終止操作絕對是佔大多數的。

2、調用棧大小限制:

    複雜的演算法一般都會使用到遞迴,例如斐波那契序列(Fibonacci sequence), 遞迴函式的執行會受到瀏覽器調用棧大小限制(Call Stack Limits)。

    何為調用棧限制?《High Performance JavaScript》裡面是這麼解釋的:

    The amount of recursion supported by JavaScript engines varies and is directly related to the size of the JavaScript call stack. With the exception of Internet Explorer, for which the call stack is related to available system memory, all other browsers have static call stack limits. The call stack size for the most recent browser versions is relatively high compared to older browsers (Safari 2, for instance, had a call stack size of 100).
    翻譯過來就是:Javascript引擎所支援的遞迴數量與調用棧大小直接相關。IE除外,它的調用棧大小與系統記憶體相關,其它瀏覽器都有固定的調用棧大小限制。大多數現代瀏覽器的調用棧大小都比老版瀏覽器要高(例如Safari2,其調用棧大小為100)。

    Figure 4-2. JavaScript call stack size in browsers(圖4-2. 瀏覽器調用棧大小)
    對瀏覽器的調用棧深淺可以作個簡單的測試:

var i = 0;
function fn() {
    fn(i++);
}
try {
    fn();
} catch (e) {
    alert(i);
}
    測試結果如下(我只拿了手頭上有的瀏覽器作了個簡單的測試,資料僅作參考):

Firefox 6.0  9015
Chrome 14
26176
Opera 11.51 32631
IE7/8  3064
IE6  1131

    從資料來看,遞迴不是無限制的,不同瀏覽器的調用棧深淺差別是很大的。遞迴調用過程,系統會為每一層返回點開闢棧來儲存,先是逐級擴充,再是收縮回溯,伴 隨遞迴次數的增多,消耗的資源也隨之增多,最終可能造成棧溢出。所以在使用遞迴時,必須要有明確的遞迴結束條件,也叫遞迴出口。

    對於重複的運算可以使用Memoization技術來緩衝上一次運算結果,以減少重複運算帶來的效能損失。Memoization技術主要是利用了散列表(或者叫索引值對)來緩衝運算結果,查詢表比執行函數要快來達到效能最佳化的目的。

    以下是 斐波那契序列(Fibonacci sequence) 的一個簡單實現:

function fibonacci(n) {
    n = parseInt(n, 10);
    if (n < 2) {
        return n;
    }
    return fibonacci(n - 1) + fibonacci(n - 2);
}
    ( 以下資料基於IE8測試)

函數 調用次數(次) 耗時(ms)
fibonacci(10) 77 0
fibonacci(20) 1891  16
fibonacci(30) 2692537   2344

    隨著n的遞增,調用次數與耗時遞增明顯。摒棄耗時不說,你會發現瀏覽器已經無法響應使用者操作了,瀏覽器UI線程已經被阻塞了。fibonacci(40)執行的時候,IE還連續彈出“是否停止運行此指令碼”提示。

    使用Memoization技術後,實現的版本如下:

var fibonacci = (function() {
    var cache = [1, 1];
    var fib = function(n) {
        if (n > 1) {
            for (var i = cache.length; i <= n; i++) {
                cache[i] = cache[i - 1] + cache[i - 2];
            }
        }
        return cache[n - 1];
    };
    return fib;
})();
    迭代取代了遞迴,沒有了瀏覽器調用棧大小限制,效能上的提升是非常明顯的。有興趣的TX可以自己對比下,效率已經不是一個數量級的了。

3、長時間運行指令碼限制

    還是上面使用Memoization技術實現的fibonacci函數,執行fibonacci(10000000),由於運算時間長,觸發了瀏覽器長時 間運行指令碼限制,UI線程被阻塞了。所以,迭代中有大規模運算,100ms內完成不了的任務可以進行拆分,讓javascript短暫讓出UI線程式控制制 權,以執行其他任務。

    要短暫讓出UI線程式控制制權,可以使用setTimeout。

    setTimeout的時間精度:
    javascript中定時器是有時間精度的, IE9(非充電模式)、IE8及其以下版本的時間精度是15.6ms;IE9(充電模式)下是4ms;其他瀏覽器一般也是4ms,低於4ms會降低電池使 用壽命。所以,setTimeout(fn, 0)並非馬上執行的,其執行時機取決於時間精度。

    為瞭解決setTimeout在不同瀏覽器的時間精度問題,W3C因此引入了新的setImmediate()函數。setImmediate與 setTimeout類似,setImmediate會在UI線程空閑時將任務插入到隊列並執行,我們不再需要關心時間精度的影響。並 且,setImmediate執行起來比setTimeout(fn, 0)要快。

    由於任務已耗用時間的不確定性,在迭代運算中,可以加上運算時間監控,決定此次迭代是否需要分割任務。

    我們來做一個簡單的demo,0~n的累加運算:

// o~n的累加運算
var test = function(n, callback) {
    var result = 0;
    var i = 0;
    (function() {
        var st = +new Date();
        for (; i < n; i++) {
            if ((+new Date()) - st < 100) {
                result++;
            } else {
                setTimeout(arguments.callee, 0); // 運算時間差大於100ms時,中斷運算,讓出UI線程式控制制權
                return;
            }
        }
        callback && callback();
    })();
};
test(10000000, showResult); // 1千萬次累加
    由於額外的流程式控制制開銷,setTimeout方式相對直接運算會消耗更多的時間,好處是UI線程不再阻塞,可以處理更多的任務。

    除了setTimeout方式,將需要長時間運算的操作放到flash裡面進行也是一種解決方案,避開javascript單一線程的影響。

    Duff's device(達夫裝置):

    Duff's device是一種加速迴圈的技巧,其思想是儘可能減少迴圈的執行次數。

/**
* Duff's Device
* http://home.earthlink.net/~kendrasg/info/js_opt/jsOptMain.html#duffsdevice
*/
var n = iterations / 8;
var caseTest = iterations % 8;
do {
    switch (caseTest) {
        case 0:
            testVal++;
        case 7:
            testVal++;
        case 6:
            testVal++;
        case 5:
            testVal++;
        case 4:
            testVal++;
        case 3:
            testVal++;
        case 2:
            testVal++;
        case 1:
            testVal++;
    }
    caseTest = 0;
} while (-- n > 0 );
    以上是Duff's device的一個實現版本。

    Duff's device將一個大迴圈分成每次迭代8次的小迴圈,8的餘數再執行一次小迴圈,以達到減少迴圈的次數。

   

    HTML5 web workers:

    隨著HTML5技術的發展,在瀏覽器UI線程外運行javascript代碼成為了可能。web workers提供了一個簡單的方式讓javascript代碼在後台線程運行而不影響UI線程。每一個web workers間都是相互獨立的,都在自己的線程中運行。

var worker = new Worker('my_task.js');
worker.onmessage = function(event) { // This event handler will be called when the worker calls its own postMessage() function
    console.log("Called back by the worker!\n");
};
worker.postMessage(); // start the worker
worker.terminate(); // terminate a running worker
    需要注意的一點是:在web workers內不能操縱DOM,可用於處理與UI線程無關的長時間運行指令碼。java-javascript 風之境地

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.