2011-11-03 6 views
17

그래서, 나는 수수께끼에 대해 생각을 해 봤는데 - 나는 어떤 이유로 대형 오브젝트 내가 노드 JS에서 통해 반복했고, 내가 그 일을하는 동안 이벤트 루프를 차단하고 싶지 않았다한다면 어떨까요?자바 스크립트에서 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으로 루프를 만드는 방법을 모르겠습니다. 내 테스트에서

, 나는 4 밀리 소요 (같은 정보를 기록,() hasOwnProperty으로 검사를) 루프 기본 반대로이 예제를 실행하기 위해 7 밀리 소요 발견했다.

그래서, Node.js를 사용하여이 같은 코드를 작성하는 가장 깨끗한/가장 빠른 방법은 무엇입니까?

+0

죄송합니다. 질문을 잘못 읽으 셨습니다. – kennebec

+1

왜요? 이것은 학대입니다. Do not do this – 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 대 process.nextTick은 중요한 차이입니다. 감사! – Jesse

+1

감사합니다. @ 제시! 'setImmediate'는'processInsetval'이나'setTimeout'과 같으며'process.setImmediate'가 아닙니다. 코드 스 니펫에서는'ReferenceError'를 피하기 위해'root.setImmediate'를 사용합니다. – skeggse

+1

웁스! 오늘 아침에 커피 마실 필요가있어! – Jesse

-1

다음은 [브라우저에서 자바 스크립트를 적용; node.js와 전혀 관련이 없을 수 있습니다. 내가 아는


두 가지 옵션 :

  1. 를 사용하여 여러 타이머 큐를 처리합니다. 그들은이 또한 ;-) 더 많은 CPU를 훔칠 수있는 좋은 방법입니다 ("더 자주 처리 항목"의 순 효과를 줄 것이다 인터리브, 또는,
  2. 는 사이클 당 더 많은 작업을 수행하거나 횟수 또는 시간을 기반으로합니다.

나는 웹 노동자 가능/적용 할 수있는 경우에 확실하지 않다.

해피 코딩.

1

여기에 언급 할 사항이 많이 있습니다.

  • 예를 들어 웹 응용 프로그램이있는 경우 해당 응용 프로그램의 프로세스에서 "과도한 작업"을하고 싶지 않을 것입니다. 알고리즘이 효율적이라 할지라도 여전히 앱 속도는 느려질 것입니다.
  • 달성하려는 목표에 따라 다음 방법 중 하나를 사용하는 것이 좋습니다.

    a) "for for"루프를 하위 프로세스에 배치하고 주 응용 프로그램에서 결과를 얻으면
    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 반복을 실행 한 다음 잠시 시스템에 제어를 반환합니다. 끝날 때까지 그만 둔 곳을 선택하십시오.

편집 : 여기가 특정 경우에 적합하다 (그리고는 인수로 전달 한 번에 수행하는 반복의 수) : 문제는 이후

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에서 완료 될 것이므로 반복 횟수에 관계없이 정확히 한 번 산출됩니다. –