nodejs并行回调设计模式

时间:2011-05-13 08:59:13

标签: javascript design-patterns concurrency node.js parallel-processing

我正试图找到一个好的模式来执行一堆并行任务。

让我来定义一些例子。任务a, b, c, d, e, f, g执行为a(function(er, ra){//task a returned, ra is result})bg

也是如此

在某项任务完成后还有一些任务应该执行,我们称之为ab, bc, abc, bd, bcd, af, fg,意味着应该执行ab返回ab(ra, rb)同时,当bc返回时,bc(rb, rc)应立即执行,如果abc全部返回,abc(ra, rb, rc)应该被执行。

对于最简单的情况,如果只有ab,我可以这样做:

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

如您所见,ab并行执行,两者完成后,ab(ra, rb)执行。

但是我怎样才能为很多并行任务做更多事情呢?

6 个答案:

答案 0 :(得分:14)

你真正想要的是一种延迟模式,尽管像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(":(");
});

答案 1 :(得分:8)

您应该查看步骤(https://github.com/creationix/step)。它只有一百多行代码,所以你可以根据需要阅读整个代码。

我喜欢的模式看起来像这样:


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

注释

  • 我的例子也使用下划线库,“匹配JQuery的礼服的领带”:http://documentcloud.github.com/underscore/
  • 命名每个步骤函数stepXXXXX是一个好习惯,这样堆栈跟踪清晰可读。
  • Step让您可以实现强大而优雅的串行和并行执行组合。这些模式简单明了,易于理解。如果你需要更复杂的东西,比如“当这些方法中的3个完成时,请转到下一步”,认真地重新设计你的设计。你真的需要这么复杂的模式吗? (也许你正在等待法定人数)。这种复杂的模式值得拥有它自己的功能。

答案 2 :(得分:2)

尝试查看step模块和this文章。

答案 3 :(得分:1)

是的,看一下流量控制模块,比如step,chain或flow~我认为在underscore.js中也有类似的东西

答案 4 :(得分:0)

一个非常简单的屏障:https://github.com/berb/node-barrierpoints

答案 5 :(得分:0)

nimble是另一个不错的选择。