шаблон проектирования параллельных обратных вызовов nodejs - PullRequest
5 голосов
/ 13 мая 2011

Я пытаюсь найти хороший шаблон для выполнения нескольких параллельных задач.

Позвольте мне дать определение некоторой задаче. Задачи a, b, c, d, e, f, g выполняются как a(function(er, ra){//task a returned, ra is result}), так что b до g

Есть также некоторые задачи, которые должны быть выполнены после выполнения какой-либо задачи, давайте назовем их ab, bc, abc, bd, bcd, af, fg, значит, когда a и b вернули ab(ra, rb), должны быть выполнены сразу, а когда b и c возвращено, bc(rb, rc) должно быть выполнено сразу, и если a, b, c все возвращено, abc(ra, rb, rc) должно быть выполнено.

Для простейшего случая, если есть только a и b, я могу сделать что-то вроде этого:

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

Как видите, a и b выполняются параллельно, а когда оба выполняются, ab(ra, rb) выполняется.

Но как я могу сделать больше вещей для множества параллельных задач?

Ответы [ 6 ]

14 голосов
/ 13 мая 2011

То, что вы на самом деле хотите, это отложенный паттерн, например 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(":(");
});
8 голосов
/ 24 августа 2011

Вы должны проверить Шаг (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 из 5 этих методов завершатся, перейдите к следующему шагу», серьезно ПЕРЕСМОТРИТЕ свой дизайн.Вы действительно нуждаетесь в таком сложном паттерне?(возможно, вы ждете кворума).Такая сложная модель заслуживает своей собственной функции.
2 голосов
/ 13 мая 2011

Попробуйте взглянуть на step module и this article.

1 голос
/ 13 мая 2011

Да, посмотрите на модуль управления потоком, например, шаг, цепь или поток ~, и я думаю, что в underscore.js тоже есть что-то подобное

0 голосов
/ 26 августа 2013

проворный - еще один хороший выбор.

0 голосов
/ 13 мая 2011

Очень простой барьер только для этого: https://github.com/berb/node-barrierpoints

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...