Javascriptを使用して、約3,500個の要素を持つXMLファイルを解析しています。 jQueryの「各」関数を使用していますが、任意の形式のループを使用できます。
問題は、ループの実行中にブラウザが数秒間フリーズすることです。コードの速度を落とさずにブラウザのフリーズを止める最良の方法は何ですか?
$(xmlDoc).find("Object").each(function() {
//Processing here
});
Forループの方が速いため、「each」関数を捨てます。 「setTimeout」を使用して待機を追加することもありますが、必要な場合にのみ頻繁に追加します。 3500レコードの処理には約17.5秒かかるため、毎回5ミリ秒待機する必要はありません。
以下は、100のレコードを処理するforループを使用した例です(5ミリ秒間隔で調整できます)。これにより、175ミリ秒のオーバーヘッドが発生します。
var xmlElements = $(xmlDoc).find('Object');
var length = xmlElements.length;
var index = 0;
var process = function() {
for (; index < length; index++) {
var toProcess = xmlElements[index];
// Perform xml processing
if (index + 1 < length && index % 100 == 0) {
setTimeout(process, 5);
}
}
};
process();
また、XML処理のさまざまな部分のベンチマークを行い、修正可能なボトルネックがあるかどうかを確認します。 firebugのプロファイラーを使用し、次のようにコンソールに書き込むことにより、firefoxでベンチマークを実行できます。
// start benchmark
var t = new Date();
// some xml processing
console.log("Time to process: " + new Date() - t + "ms");
お役に立てれば。
処理の間にtimeOutを設定して、ループサイクルがすべてのブラウザリソースを使い果たすことを防ぎます。合計すると、すべてを処理してループするのに数秒しかかからず、3,500の要素に対しては不合理ではありません。
var xmlElements = $(xmlDoc).find('Object');
var processing = function() {
var element = xmlElements.shift();
//process element;
if (xmlElements.length > 0) {
setTimeout(processing, 5);
}
}
processing();
GetgoからJSにネイティブであるように、3500要素をxmlからJSONサーバーサイドに変換するか、変換したサーバーにアップロードすることを検討します。
これにより、負荷が最小限に抑えられ、ファイルサイズも小さくなります。
Turboidフレームワークを使用すると、ブラウザーをフリーズせずに長いループを作成できます。これにより、次のようなコードを作成できます。
loop(function(){
// Do something...
}, number_of_iterations, number_of_milliseconds);
このturboid.net記事の詳細: Javascriptの実際のループ
ゼロの期間でsetTimeout()を設定すると、必要に応じて生成されます
Javascriptはシングルスレッドなので、setTimeout
は別として、できることはあまりありません。 Google Gearsを使用することがサイトのオプションである場合、真のバックグラウンドスレッドでjavascriptを実行する機能を提供します。
HTML5ワーカーAPIを使用できますが、これはFirefox 3.1およびSafari 4ベータatmでのみ機能します。
あなたはコードを短くすることを試みることができます
$(xmlDoc).find("Object").each(function(arg1) {
(function(arg1_received) {
setTimeout(function(arg1_received_reached) {
//your stuff with the arg1_received_reached goes here
}(arg1_received), 0)
})(arg1)
}(this));
これはあまりあなたに害を与えません;)
ユーザーがページを連続して更新したときにも同じ問題が発生しました。その理由は、2つのネストされたforループが52000回以上発生したためです。この問題は、Firefox 24でChrome 29よりもFirefoxが早くクラッシュする(Chromeよりも約2000ミリ秒早く)ので、より厳しいものでした。それぞれのコードをリファクタリングして、ループ配列全体を4つの別々の呼び出しに分割し、結果を1つにマージしました。
このようなもの:
var entittiesToLoop = ["..."]; // Mainly a big array
loopForSubset(0, firstInterval);
loopForSubset(firstInterval, secondInterval);
...
var loopForSubset = function (startIndex, endIndex) {
for (var i=startIndex; i < endIndex; i++) {
//Do your stuff as usual here
}
}
私のために働いた他のソリューションは、Worker APIs
HTML5から。ワーカーでメインスレッドのバックグラウンドで実行されるため、ブラウザーがフリーズするのを防ぐため、ワーカーで同じコンセプトを使用します。これをWorkers APIで適用してもうまくいかない場合は、loopForSubset
の各インスタンスを異なるワーカーに配置し、結果をWorkerのメインの呼び出し元にマージします。
これは完璧ではないかもしれませんが、これはうまくいきました。誰かがまだこれがそれらに合うかもしれないと思うなら、私はより本当のコードチャンクを手伝うことができます。
@ tj111の修正として、使用可能なコード全体に回答する
//add pop and shift functions to jQuery library. put in somewhere in your code.
//pop function is now used here but you can use it in other parts of your code.
(function( $ ) {
$.fn.pop = function() {
var top = this.get(-1);
this.splice(this.length-1,1);
return top;
};
$.fn.shift = function() {
var bottom = this.get(0);
this.splice(0,1);
return bottom;
};
})( jQuery );
//the core of the code:
var $div = $('body').find('div');//.each();
var s= $div.length;
var mIndex = 0;
var process = function() {
var $div = $div.first();
//here your own code.
//progress bar:
mIndex++;
// e.g.: progressBar(mIndex/s*100.,$pb0);
//start new iteration.
$div.shift();
if($div.size()>0){
setTimeout(process, 5);
} else {
//when calculations are finished.
console.log('finished');
}
}
process();