Параллельный шаблон обратного вызова 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)
выполните.
Но как я могу делать больше вещей для множества параллельных задач?
Ответы
Ответ 1
То, что вы на самом деле хотите, - это отложенный шаблон, хотя 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 e ?
Futures.join(df,dg).when(fg);
Futures.join(da,db,dc,dd,de,df,dg).fail(function() {
console.log(":(");
});
Ответ 2
Вы должны проверить Шаг (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 tux": http://documentcloud.github.com/underscore/
- Именование каждой функции шага stepXXXXX - хорошая привычка, так что трассировка стека понятна и читаема.
- Шаг позволяет создавать мощные и элегантные комбинации серийного и параллельного исполнения. Эти шаблоны просты и понятны. Если вам нужно что-то более сложное, например, "когда 3 из 5 из этих методов завершатся, переходите к следующему шагу", серьезно РАСШИРИТЕ ваш дизайн. вам действительно нужна такая сложная модель? (возможно, вы ждете набора кворума). Такая сложная модель заслуживает своей собственной функции.
Ответ 3
Попробуйте посмотреть step модуль и this статьи.
Ответ 4
Да, посмотрите модуль управления потоком, например, шаг, цепочку или поток ~, и я думаю, что есть что-то подобное в underscore.js тоже
Ответ 5
Очень простой барьер только для этого: https://github.com/berb/node-barrierpoints
Ответ 6
nimble - еще один хороший выбор.