2011-11-03 17 views
17

Entonces, he estado pensando en un juego de ingenio: ¿qué pasaría si tuviera un objeto grande que por alguna razón tuviera que repetir en el nodo js y no quisiera bloquear el ciclo de eventos mientras lo hacía?¿Cuál es la forma más limpia de escribir un ciclo sin bloqueo en javascript?

He aquí un ejemplo fuera de la parte superior-de-mi-cabeza, estoy seguro de que puede ser mucho más limpio:

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

Aunque estoy seguro de que hay otras razones para que sea desordenado, esto se ejecutará más lento que un bucle for normal, porque setInterval 0 en realidad no se ejecuta cada 0 ms, pero no estoy seguro de cómo hacer un bucle con el proceso mucho más rápido.nextTick.

En mis pruebas, encontré que este ejemplo demora 7 ms en ejecutarse, a diferencia de un bucle for nativo (con checks de hasOwnProperty(), que registran la misma información), que demora 4 ms.

Entonces, ¿cuál es la forma más limpia y rápida de escribir este mismo código usando node.js?

+0

Lo siento- leyó mal la pregunta – kennebec

+1

¿Por qué? Esto es abuso. No hagas esto – Raynos

+3

@Raynos - ¿Por qué es este abuso? Si realizo una iteración a través de un objeto gigante con un bucle for nativo, bloqueo el hilo todo el tiempo que el bucle tarde en ejecutarse. Eso suena como abuso para mí, si tomo 25 ms para servir a cada usuario, eso se puede traducir en un gran problema de escalado. – Jesse

Respuesta

3

El comportamiento de process.nextTick ha cambiado desde que la pregunta era preguntó. Las respuestas anteriores tampoco siguieron la pregunta según la limpieza y la eficiencia de la función.

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

hacer tomar nota de @alessioalex's comments respecto Kue y buen trabajo en cola.

Ver también: share-time, un módulo que escribí para hacer algo similar a la intención de la pregunta original.

+0

Es bueno ver esta pregunta revisitada con la última información. process.setImmediate vs process.nextTick es una distinción importante. ¡Gracias! – Jesse

+1

Gracias, @Jesse! Tenga en cuenta que es 'setImmediate', como' setInterval' o 'setTimeout', no' process.setImmediate'. En el fragmento de código, uso 'root.setImmediate' para evitar un' ReferenceError'. – skeggse

+1

¡Vaya! ¡Necesito tomar mi café esta mañana! – Jesse

-1

Lo siguiente se aplica a [navegador] JavaScript; puede ser completamente irrelevante para node.js.


dos opciones que conozco:

  1. Use varios temporizadores para procesar la cola. Se intercalarán lo que dará el efecto neto de "procesar elementos con más frecuencia" (esta es también una buena manera de robar más CPU ;-) o
  2. Haga más trabajo por ciclo, ya sea por tiempo o por conteo.

No estoy seguro de si Web Workers es aplicable/disponible.

Happy coding.

1

Hay muchas cosas que decir aquí.

  • Si tiene una aplicación web, por ejemplo, no le gustaría hacer un "trabajo pesado" en el proceso de esa aplicación. Aunque su algoritmo sea eficiente, probablemente ralentice la aplicación.
  • Dependiendo de lo que estamos tratando de lograr, es probable que utilice uno de los siguientes enfoques:

    a) poner su "en el" bucle en un proceso hijo y obtener el resultado en su aplicación principal una vez se acabó
    b) si está tratando de lograr algo como trabajos retrasados ​​(por ejemplo, enviando correos electrónicos) debe intentar https://github.com/LearnBoost/kue
    c) hacer un programa propio de Kue usando Redis para comunicarse entre la aplicación principal y el "trabajo pesado" "aplicación.

Para estos enfoques también puede usar múltiples procesos (para simultaneidad).

Ahora el tiempo para un código de ejemplo (que puede no ser perfecta, por lo que si usted tiene una mejor sugerencia por favor corríjanme):

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

Me preocuparía que todo el conjunto de sectores esté sucediendo. ¿Hiciste un benchmark de esto? –

+0

No lo comparó, pero definitivamente podría ir por ahí y usar una técnica diferente (manteniendo la misma idea). – alessioalex

1

En lugar de:

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

hacer algo como esto :

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

Esto ejecutará 100 iteraciones del ciclo, luego devolverá el control al sistema por un momento, th es recoger donde lo dejó, hasta que esté hecho.

Editar: aquí está adaptada para su caso particular (y con el número de iteraciones que realiza a la vez que se pasa como un argumento):

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

¿De hecho lo has probado? Todo el trabajo se realizará en el primer 'nextTick', produciéndose exactamente una vez independientemente del número de iteraciones. –

Cuestiones relacionadas