2013-05-03 2 views
5

私は長い実行機能があります。大きな配列を繰り返し処理し、各ループ内で関数を実行します。ほとんどの部分についてどのようにjavascriptで長期実行機能を分割するが、パフォーマンスを維持する

longFunction : function(){ 
     var self = this; 
     var data = self.data; 

     for(var i=0; len = data.length; i<len; i++){ 
       self.smallFunction(i); 
     } 
}, 
smallFunction : function(index){ 

// Do Stuff! 

} 

これは結構ですが、私は周りの1500かそこら以上のアレイを取り扱っておりますときに我々は、JavaScriptの実行警告メッセージが供給のポイントを得ます。

だから私はこれを解読する必要があります。私の最初の試みはそうのようなものです:

longFunction : function(index){ 
     var self = this; 
     var data = self.data; 


     self.smallFunction(index); 

     if(data.slides[index+1){ 
     setTimeout(function(){ 
      self.longFunction(index+1); 
     },0); 
     } 
     else { 
       //WORK FINISHED 
     } 

}, 
smallFunction : function(index){ 

// Do Stuff! 

} 

だからここに私はループを削除し、各反復そのインデックスを増加させ、自己の呼び出し機能を導入しています。メインのUIスレッドに制御を戻して、JavaScriptの実行警告メソッドを防ぐために、私はsetTimeoutを追加して、各繰り返しの後に更新する時間を許しました。問題は、この方法で実際の作業を完了させるには文字通り10倍の時間がかかることです。何が起きているかは、setTimeoutが0に設定されているにもかかわらず、実際には10msほど待っています。大規模なアレイでは非常に迅速に構築されます。 setTimeoutを削除して、longFunction呼び出しを行うと、元のループ方法に匹敵するパフォーマンスが得られます。

私は別の解決策が必要ですが、これはループと同等のパフォーマンスを持ちますが、JavaScriptの実行警告は発生しません。残念ながら、この例ではwebWorkersは使用できません。

このプロセスで完全に反応するUIは必要ありません。数秒ごとにプログレスバーを更新するだけで十分です。

ループの塊に分割するのはオプションですか?私。一度に500回繰り返す、停止する、タイムアウトする、進行状況バーを更新する、次の500を実行するなど..

もっと良い点はありますか?

ANSWER:

唯一の解決策は、仕事をチャンクしているようです。それがある場合は、次のインデックスは、250によってdivisbleであれば、私はここでやっているすべてをチェックしている

longFunction : function(index){ 
      var self = this; 
      var data = self.data; 


      self.smallFunction(index); 

      var nextindex = i+1; 

      if(data.slides[nextindex){ 
      if(nextindex % 250 === 0){ 
      setTimeout(function(){    
       self.longFunction(nextindex); 
      },0); 
      } 
      else { 
       self.longFunction(nextindex); 
      } 
      } 
      else { 
        //WORK FINISHED 
      } 

    }, 
    smallFunction : function(index){ 

    // Do Stuff! 

    } 

:私はUIは、すべて250回の反復を更新できるようにしています私の自己の呼び出し元の関数に以下を追加することにより

メインUIスレッドを更新できるようにタイムアウトを使用します。そうでなければ、我々は直接それを直接呼び出す。問題が解決しました!

+0

それはバックグラウンドで非同期的にそれを実行することは可能ですか?あなたがそれを行うことができない場合、はい、それを塊に分解してください。 – Patashu

+0

あなたの機能は各項目に対して正確に何をしていますか?一度に複数のsetTimeoutを実行することによって、それらを並列化することができます。 – exebook

+1

@Patashu - Webワーカーはまだすべてのブラウザで利用できるわけではありません...したがって、合理的な塊に侵入することは安全なアプローチのようです。 –

答えて

1

ここで私が書いていた以前の回答から変更され、いくつかのバッチ処理コードです:あなたは、単にこれを行うことができますので

var n = 0, 
    max = data.length; 
    batch = 100; 

(function nextBatch() { 
    for (var i = 0; i < batch && n < max; ++i, ++n) { 
     myFunc(n); 
    } 
    if (n < max) { 
     setTimeout(nextBatch, 0); 
    } 
})(); 
+0

あなたの方法と私がちょうど私の質問に追加した方法の両方が完全に機能します。関数のオーバーヘッドを避けるため、私はあなたのほうが少し速いと期待しています。ありがとうalnitak。 – gordyr

2

実は1500タイムアウトは、何もありません:

var i1 = 0 
for (var i = 0; i < 1500; i++) setTimeout(function() { doSomething(i1++) }, 0) 

システムはタイムアウトイベントをキューに入れますあなたのために彼らはすぐに相次いで呼び出されます。また、ユーザーが実行中に何かをクリックすると、遅れに気づかないでしょう。そして、「スクリプトは長時間走っていません」ということはありません。

私の実験から、V8は毎秒50万のタイムアウトを作成できます。

UPDATE

あなたのワーカー機能するために渡されたi1が必要な場合は、ちょうどそれを持つオブジェクトを渡し、そしてあなたの関数の内部でカウンタをインクリメント。

function doSomething(obj) { 
    obj.count++ 
    ...put actual code here 
} 
var obj = {count: 0} 
for (var i = 0; i < 1500; i++) setTimeout(function() { doSomething(obj) }, 0) 

Node.jsの下には、使用setImmediate(...)をasloすることができます。

+0

それは正しいですが、私たちは繰り返しごとにタイムアウトを使用しているので、大量にすべての処理が遅くなります。ほとんどのブラウザでは、0タイムアウトは実際には約10msに相当します。つまり、2000の長いプロセスセットでは、必要でないロード時間に20秒を追加します。私たちが考え出した解決策は、特定の間隔でタイムアウトを実行するだけで、他の人が即座に発火することができます。 – gordyr

+0

私はあなたがこの解決策を理解していないと確信しています。 setTimeoutsは同時にすべて一緒に開始します。最初のものが実行を開始する前に、わずかな時間が経過します。しかし、他の人はそれを遅らせることなくそれに従うでしょう。余分に20秒はかかりません。 – exebook

+0

私は1500のタイムアウトが何もないと言ったとき、私は1500 * 10msがそうではないことを意味しませんでした。 V8は何千ものタイムアウトの待ち行列を簡単に保持することができ、その時間はすでに来ているということです!すぐに1つずつ実行されます。 – exebook

0

もっと良い点はありますか?

最新のブラウザでしか動作していないのであれば、バックグラウンドでJSを実行できる「Webワーカー」を調べる必要があります。

https://developer.mozilla.org/en-US/docs/DOM/Using_web_workers

+0

私はウェブワーカーに多くの経験を持っていますが、残念ながらDOMアクセス制限のためにこの状況には適用されません。したがって、使用できません。 – gordyr

関連する問題