前面一篇討論了XHR對象已經異步的部分觀點,異步能解決許多長時間運行交互問題,但它絕不是適用於任何地方。
長時間運行的原因
javascript運行在瀏覽器環境中,因而分配的資源數量是十分有限的,不同於桌面程序能隨意控制它們想要的內存大小和CPU時間,Javascript被嚴格限制了。甚至長時間運行腳本都有一定約束,假如代碼運行超過了特定的時間限制或者運行的語句數量超出特定約束,瀏覽器就會彈出錯誤提示框,詢問是繼續運行還是停止它。
出現這種情況的原因主要有兩個:過長或過深嵌套的函數調用;進行了大量的迭代處理。
for(var i = 0, len = obj.length; i < len; i++){ process(obj[i]); }
上面代碼為最常見的迭代情況,迭代次數未定,同時所有的迭代都是同步阻塞進行,也即是前一次迭代處理完成之后才能進行下一次迭代。
- 該迭代是否必須同步進行呢?假如數據的處理會造成其他代碼運行的阻塞,那么答案是:必須同步進行。
- 迭代是否有必須按特定的順序呢?通常情況下,迭代的數據集合沒有固定的順序,也就是說打亂順序代碼的運行。
分塊處理
當你遇到某個迭代占用了大量時間,並且上述兩點都是非必須,那么你可以嘗試分割這個循環,也即是數組分塊,小塊小塊地處理數組。基本的思路是:為要處理的項目建立一個queue,然后使用setTimeout取出下一個要處理的數據進行處理,接着另外設置一個setTimeout進行迭代。
function chunk(array, process, context){ setTimeout(function(){ var item = array.shift(); //取出下一個數據 process.call(context, item); !!array.length && setTimeout(arguments.callee, 100); }, 100); }
array為要處理的數據集合,process為處理數據的函數,context為執行的上下文。時間間隔設置為100ms,使得javascript進程有時間在處理數據的事件之間轉入空閑,當然你可以根據你的需要修改這個間隔時間。
var data = [222,333,234,342,4536,5674,5634,2342,342,643,344,234,342,4536,5674,5634,2342,234,342,4536,5674,5634,2342]; function print(item){ var div = document.getElementById('div'); div.innerHTML += item + "<br />"; } function chunk(arr, process, context){ setTimeout(function(){ var item = arr.shift(); process.call(context, item); !!arr.length && setTimeout(arguments.callee, 100); }, 100); } chunk(data.concat(), print);
注意,最后調用的時候,傳遞的是隊列數組的副本,因為shift方法會改變原數組,當然要是不在意原數組的改變,就可以忽略這點。
優勢
數組分塊讓繁瑣的項目分拆成多個小塊,按隊列分開執行,在每個小塊處理之后,給予瀏覽器處理其他的機會,這樣可以避免長時間運行腳本的錯誤出現。當某個函數運行時間超過200ms以上,就可以考慮使用分塊方式進行處理。
其他
另外,瀏覽器中某些DOM操作非常的頻繁,若是連續過多的變化容易引起瀏覽器掛起,例如:resize事件,當瀏覽器發生改變,onresize事件將會被連續觸發,導致時間頻繁被調用。這樣的情況,可以使用分塊升級版。也就是將連續的同步代碼拆分,讓執行函數的請求停止一段時間之后才執行。
function throttle(method, context){ clearTimeout(method.tId); method.tId = setTimeout(function(){ method.call(context); }, 100); }
定時器的ID儲存在方法的屬性tId之中,每次調用都會先清除前一次的定時器ID,以阻止之前的調用被執行。時間間隔設置為100ms,也就是,100ms之內,不管被調用了多少次,但只執行一次,之前的多余次數都被清除。
window.onresize = function(){throttle(resizeFunc);};
參考鏈接:http://www.nczonline.net/blog/2009/06/05/speed-up-your-javascript-the-talk/