2011-11-03 16 views
17

だから、私は脳のお誘いを考えていました。なぜなら何らかの理由で大きなオブジェクトをノードjで繰り返しなければならなかったのですが、その間にイベントループをブロックしたくないのでしょうか? 、私はそれが散らかっているため、他の理由があると確信しているもののjavascriptでnon-blocking forループを書く最もクリーンな方法は何ですか?

var forin = function(obj,callback){ 
    var keys = Object.keys(obj), 
     index = 0, 
     interval = setInterval(function(){ 
      if(index < keys.length){ 
       callback(keys[index],obj[keys[index]],obj); 
      } else { 
       clearInterval(interval); 
      } 
      index ++; 
     },0); 
} 

:ここ

は、オフ・トップ・オブ・私の頭の例ですが、私はそれがはるかにきれいにすることができます確信していますsetInterval 0は実際には0msごとに実行されないので、これは通常のforループよりも遅く実行されますが、はるかに高速なprocess.nextTickでループを作成する方法がわかりません。

私のテストでは、ネイティブforループ(hasOwnProperty()チェック、同じ情報を記録する)とは異なり、この例では7ミリ秒かかることがわかりました。

node.jsを使用してこの同じコードを書く最もクリーンで最速の方法は何ですか?

+0

申し訳ありません質問を誤読 – kennebec

+1

なぜですか?これは虐待です。これをしないでください – Raynos

+3

@レイノス - なぜこの乱用ですか?ネイティブのforループを持つ巨大なオブジェクトを反復処理すると、ループが実行される限りスレッドはブロックされます。それは私に悪用のように聞こえる、私はすべてのユーザーにサービスするために25ミリ秒かかる場合、それは膨大なスケーリングの問題に変換することができます。 – Jesse

答えて

3

process.nextTickの動作が変更されました尋ねたこれまでの回答は、機能の清潔さと効率性の点で問題には追いつかなかった。

// in node 0.9.0, process.nextTick fired before IO events, but setImmediate did 
// not yet exist. before 0.9.0, process.nextTick between IO events, and after 
// 0.9.0 it fired before IO events. if setImmediate and process.nextTick are 
// both missing fall back to the tick shim. 
var tick = 
    (root.process && process.versions && process.versions.node === '0.9.0') ? 
    tickShim : 
    (root.setImmediate || (root.process && process.nextTick) || tickShim); 

function tickShim(fn) {setTimeout(fn, 1);} 

// executes the iter function for the first object key immediately, can be 
// tweaked to instead defer immediately 
function asyncForEach(object, iter) { 
    var keys = Object.keys(object), offset = 0; 

    (function next() { 
    // invoke the iterator function 
    iter.call(object, keys[offset], object[keys[offset]], object); 

    if (++offset < keys.length) { 
     tick(next); 
    } 
    })(); 
} 

Kueと適切なジョブキューイングについては、@alessioalex's commentsをメモしてください。

次も参照してください:share-time、私は元の質問の意図と同様のことをするために書いたモジュールです。

+0

最新情報でこの質問を再訪してください。 process.setImmediateとprocess.nextTickは重要な違いです。ありがとう! – Jesse

+1

ありがとう、@ジェス! 'setImmediate'は' setInterval'や 'setTimeout'のように' process.setImmediate'ではなく、注意してください。コードスニペットでは、 'ReferenceError'を避けるために' root.setImmediate'を使います。 – skeggse

+1

おっと!今朝私のコーヒーを飲む必要がある! – Jesse

-1

[ブラウザ] JavaScriptには以下が適用されます。 node.jsとまったく無関係かもしれません。

  1. キューを処理するために、複数のタイマーを使用します。私の知っているオプションは


    つ。これらはインターリーブされ、「アイテムをより頻繁に処理する」という正味の効果が得られます(これはさらにCPUを奪うための良い方法です)、または

  2. 回数または時間ベースでさらに多くの作業を行います。

Webワーカーが該当するかどうかはわかりません。

ハッピーコーディング。

1

ここには多くのことがあります。

  • たとえば、Webアプリケーションを使用している場合は、そのアプリケーションのプロセスで重労働をしたくない場合があります。あなたのアルゴリズムは効率的ですが、それでもアプリを遅くする可能性は非常に高いです。
  • 達成しようとしていることに応じて、次のいずれかの方法を使用することになります。

    a) "for for"ループを子プロセスに配置し、
    b)遅延ジョブのようなものを達成しようとしている場合は、https://github.com/LearnBoost/kue
    c)メインアプリケーションと「重い荷物」の間で通信するためにRedisを使用して独自のKueのようなプログラムを作ってください。 "app。

これらのアプローチでは、複数のプロセス(並行処理用)を使用することもできます。

今のサンプルコード(あなたはより良い提案を持っている場合は、私を修正してくださいので、それは、完璧ではないかもしれない)のための時間:

var forIn, obj; 

// the "for in" loop 
forIn = function(obj, callback){ 
    var keys = Object.keys(obj); 
    (function iterate(keys) { 
    process.nextTick(function() { 
     callback(keys[0], obj[keys[0]]); 
     return ((keys = keys.slice(1)).length && iterate(keys)); 
    }); 
    })(keys); 
}; 

// example usage of forIn 
// console.log the key-val pair in the callback 
function start_processing_the_big_object(my_object) { 
    forIn(my_object, function (key, val) { console.log("key: %s; val: %s;", key, val); }); 
} 

// Let's simulate a big object here 
// and call the function above once the object is created 
obj = {}; 
(function test(obj, i) { 
    obj[i--] = "blah_blah_" + i; 
    if (!i) { start_processing_the_big_object(obj); } 
    return (i && process.nextTick(function() { test(obj, i); })); 
})(obj, 30000); 
+1

アレイスライスがすべて起こっているのを心配しています。これをベンチマークしましたか? –

+0

ベンチマークはしませんでしたが、必ずスライスして別の手法を使用してください(同じアイデアを維持してください)。 – alessioalex

1

の代わりに:

for (var i=0; i<len; i++) { 
    doSomething(i); 
    } 

はこのような何かを行います:

これは、ループの100回の反復を実行し、次に制御をシステムに戻します。それが終わるまで中断したところで拾う。

編集:ここではそれはあなたの特定のケースのために適合されている(そしてそれは、引数として渡された時に実行反復回数で):質問があったため

var forin = function(obj, callback, numPerChunk){ 
    var keys = Object.keys(obj); 
    var len = keys.length; 
    var i = 0, limit; 
    while (i < len) { 
    limit = i + numPerChunk; 
    if (limit > len) 
     limit = len; 
    process.nextTick(function(){ 
     for (; i<limit; i++) { 
      callback(keys[i], obj[keys[i]], obj); 
     } 
     }); 
    } 
} 
+0

これは実際にテストしましたか?すべての作業は最初の 'nextTick'で完了し、反復回数にかかわらず正確に1回だけ生成されます。 –

関連する問題