Шаблон проектирования параллельного обратного вызова nodejs
Я пытаюсь найти хороший шаблон для выполнения нескольких параллельных задач.
Позвольте мне определить некоторые задачи для примера. Задачи 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 ответов
То, что вы на самом деле хотите, это отложенный паттерн, как фьючерсы.
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(":(");
});
Вы должны проверить Шаг ( 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 - это хорошая привычка, поэтому трассировка стека будет четкой и читаемой.
- Шаг позволяет создавать мощные и элегантные комбинации последовательного и параллельного исполнения. Эти шаблоны просты и понятны. Если вам нужно что-то более сложное, например, "когда 3 из 5 этих методов завершатся, перейдите к следующему шагу", серьезно ПЕРЕСМОТРИТЕ свой дизайн. вам действительно нужен такой сложный шаблон? (возможно, вы ждете набора кворума). Такой сложный паттерн заслуживает своей собственной функции.
Да, посмотрите на модуль управления потоком, например, step, chain или flow ~, и я думаю, что что-то подобное есть и в underscore.js
Очень простой барьер только для этого: https://github.com/berb/node-barrierpoints