5

Estoy tratando de encontrar un buen patrón para ejecutar un conjunto de tareas paralelas.nodejs patrón de diseño de devolución de llamada paralelo

Déjenme definir algunas tareas para ejemplificar. Tareas a, b, c, d, e, f, g ejecutan como a(function(er, ra){//task a returned, ra is result}), también lo hacen b a g

También hay algunas tareas que deben ejecutar después de una cierta tarea se realiza, llamémosles ab, bc, abc, bd, bcd, af, fg, significa que cuando a y b ha vuelto ab(ra, rb) deberían ser ejecutados a la vez, y cuando b y c se devolvieron, bc(rb, rc) se deben ejecutar a la vez, y si a, b, c todos devueltos, abc(ra, rb, rc) deben ejecutarse.

Para el caso más simple, si sólo hay a y b, yo puedo hacer algo como esto:

(function(cb){ 
    var count = 2, _ra, _rb; 
    function update(){if(--count == 0) cb(null, _ra, _rb)} 
    a(function(er, ra){_ra = ra; update()}); 
    b(function(er, ra){_rb = rb; update()}); 
})(function(er, ra, rb){ 
    ab(ra, rb); 
}); 

Como se puede ver, a y b ejecutar en paralelo, y cuando los dos lo hacen, ab(ra, rb) ejecutar.

Pero, ¿cómo puedo hacer más cosas para muchas tareas paralelas?

Respuesta

2

Intente mirar el módulo step y el artículo this.

+0

Eso debería ser un cumplido.Explicar cómo usar el paso es una respuesta. – Raynos

+2

@Raynos me llevó un par de veces leer eso para darme cuenta creo que querías decir 'comentario', pero no lo cambies porque es bastante gracioso – BigOmega

1

Sí, mira al módulo de control de flujo, como paso, la cadena o el flujo ~ y creo que es algo así en underscore.js demasiado

+1

que algo es '_.después' – Raynos

14

lo que realmente quiere es un patrón diferida aunque como futures.

function defer(f) { 
    // create a promise. 
    var promise = Futures.promise(); 
    f(function(err, data) { 
     if (err) { 
      // break it 
      promise.smash(err); 
     } else { 
      // fulfill it 
      promise.fulfill(data); 
     } 
    }); 
    return promise; 
} 
var da = defer(a), db = defer(b), dc = defer(c), dd = defer(d), de = defer(e), df = defer(f), dg = defer(g); 

// when a and b are fulfilled then call ab 
// ab takes one parameter [ra, rb] 
Futures.join(da, db).when(ab); 
Futures.join(db, dc).when(bc); 
// abc takes one parameter [ra, rb, rc] 
Futures.join(da, db, dc).when(abc); 
Futures.join(db, dd).when(bd); 
Futures.join(db, dc, dd).when(bcd); 
Futures.join(da, df).when(af); 
// where's e ? 
Futures.join(df,dg).when(fg); 
Futures.join(da,db,dc,dd,de,df,dg).fail(function() { 
    console.log(":("); 
}); 
+6

Es casi como música es tan hermoso – jcolebrand

+0

Es esto realmente manejar esas tareas en paralelo, cuando node.js se ejecuta en un solo proceso en un solo proceso? ¿O está empujando las tareas al final de la cola de eventos, para ser manejadas más tarde, cuando la cola de eventos no tiene nada más que hacer? –

+0

@AntKutschera no hay multi threading. Simplemente dice ejecutar a-g en paralelo (hay un proceso asincrónico manejado por un proceso diferente en un hilo diferente) y cuando cualquier par termina ejecuta esta función – Raynos

9

Debe retirar Paso (https://github.com/creationix/step). Es un poco más de cien líneas de código, por lo que puede leer todo si es necesario.

Mi patrón preferido es como la siguiente:


function doABunchOfCrazyAsyncStuff() { 
    Step (
    function stepA() { 
     a(arg1, arg2, arg3, this); // this is the callback, defined by Step 
    } 
    ,function stepB(err, data) { 
     if(err) throw err; // causes error to percolate to the next step, all the way to the end. same as calling "this(err, null); return;" 
     b(data, arg2, arg3, this); 
    } 
    ,function stepC(err, data) { 
     if(err) throw err; 
     c(data, arg2, arg3, this); 
    } 
    ,function stepDEF(err, data) { 
     if(err) throw err; 
     d(data, this.parallel()); 
     e(data, this.parallel()); 
     f(data, this.parallel()); 
    } 
    ,function stepGGG(err, dataD, dataE, dataF) { 
     if(err) throw err; 
     var combined = magick(dataD, dataE, dataF); 
     var group = this.group(); // group() is how you get Step to merge multiple results into an array 
     _.map(combined, function (element) { 
     g(element, group()); 
     }); 
    } 
    ,function stepPostprocess(err, results) { 
     if(err) throw err; 
     var processed = _.map(results, magick); 
     return processed; // return is a convenient alternative to calling "this(null, result)" 
    } 
    ,cb // finally, the callback gets (err, result) from the previous function, and we are done 
); 
} 

Notas

  • Mi ejemplo también utiliza la biblioteca de subrayado, "el lazo para que coincida con esmoquin de jQuery": http://documentcloud.github.com/underscore/
  • Naming cada paso la función stepXXXXX es un buen hábito para que las trazas de la pila sean claras y legibles.
  • Step le permite realizar combinaciones potentes y elegantes de ejecución en serie y paralela. Estos patrones son claros y comprensibles. Si necesita algo más complejo como "cuando se complete 3 de 5 de estos métodos, vaya al siguiente paso", VUELVA A PONER EN VERDAD SU DISEÑO. ¿Tiene realmente necesita un patrón tan complejo? (Tal vez está esperando un conjunto de quórum). Un patrón tan complejo merece una función propia.
+0

es que las tareas de grupo son paralelas o en serie –

+0

Hay algunas desventajas en la asincronía, es decir, si desea llamar a un método de utilidad, como con cascada, debe tener un contenedor para manejar el error. ex: collection.find.bind (...), donde puede usar carpetas de funciones para métodos en su cadena ... esto es realmente útil, y no funciona con el paso. – Tracker1

0

nimble es otra buena opción.

Cuestiones relacionadas