2011-11-03 115 views
17

因此,我一直在考慮一個腦筋急轉彎 - 如果我有一個大對象,出於某種原因,我不得不在節點js中迭代,並且不想在我這樣做時阻止事件循環?在JavaScript中編寫非阻塞循環的最簡潔方法是什麼?

這裏是現成的,頂級的我頭一個例子,我敢肯定,這可能是更清潔:

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實際上並不是每0毫秒執行一次,但我不確定如何使用快得多的process.nextTick進行循環。

在我的測試中,我發現這個例子需要7 ms運行,而不是本地for循環(hasOwnProperty()檢查,記錄相同的信息),這需要4 ms。

那麼,使用node.js編寫相同代碼的最簡潔/最快的方法是什麼?

+0

對不起 - 誤讀了這個問題 – kennebec

+1

爲什麼?這是濫用。不要這樣做 – Raynos

+3

@Raynos - 爲什麼這會被濫用?如果我使用本機for循環遍歷一個巨大的對象,只要循環執行就會阻塞該線程。這聽起來像是對我的濫用,如果我花25ms爲每個用戶服務,那可能會導致一個巨大的擴展問題。 – 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 vs process.nextTick是一個重要的區別。謝謝! – Jesse

+1

謝謝,@Jesse!請注意,它是'setImmediate',就像'setInterval'或'setTimeout',而不是'process.setImmediate'。在代碼片段中,我使用'root.setImmediate'來避免'ReferenceError'。 – skeggse

+1

哎呀!今天早上需要喝我的咖啡! – Jesse

-1

以下內容適用於[瀏覽器] JavaScript;它可能與node.js完全無關。


兩個選項,我知道的:

  1. 使用多個計時器來處理隊列。它們會交織,這會產生「更頻繁地處理項目」的淨效果(這也是一種竊取更多CPU的好方法;-),或者,每個週期執行更多的工作,無論是基於計數還是基於時間。

我不確定Web Workers是否適用/可用。

快樂編碼。

1

這裏有很多事情要說。

  • 如果您有一個Web應用程序,例如,您不想在該應用程序的過程中執行「繁重工作」。即使你的算法是高效的,它仍然可能會減慢應用程序的速度。
  • 根據你想要達到的目標,你可能會使用以下方法之一:

    a)將你的「for in」循環放入子進程,並在結束後在主應用程序中得到結果
    b)如果你正在嘗試實現類似延遲工作(用於發送電子郵件),你應該嘗試https://github.com/LearnBoost/kue
    c)使用Redis製作一個類似Kue的程序,以在主應用程序和「繁重的工作「應用程序。

對於這些方法,您還可以使用多個進程(併發)。

現在時間爲示例代碼(它可能不是完美的,所以如果你有更好的建議,請指正):

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); 
    } 

做這樣的事情:

var i = 0, limit; 
while (i < len) { 
    limit = (i+100); 
    if (limit > len) 
    limit = len; 
    process.nextTick(function(){ 
    for (; i<limit; i++) { 
     doSomething(i); 
    } 
    }); 
    } 
} 

這將運行100次迭代的循環,然後返回控制系統一會兒,th在它停止的地方,直到它完成。

編輯:這裏是適合你的特殊情況下(與迭代次數它執行在傳遞中作爲自變量的時間):

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'中完成,無論迭代的次數如何,都會產生一次。 –

相關問題