5

我試圖找到一個很好的模式來執行一堆並行任務。nodejs並行回調設計模式

讓我定義一些任務來舉例說明。任務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),並且如果a,b,c全部返回,則應執行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)執行。

但是我怎麼能爲很多並行任務做更多的事情呢?

回答

2

試着看看step模塊和this的文章。

+0

這應該是一種恭維。解釋如何使用步驟是一個答案。 – Raynos 2011-05-13 15:54:42

+2

@Raynos @Raynos花了我幾次閱讀,意識到我認爲你的意思是'評論',但不要改變它,因爲這很有趣 – BigOmega 2013-01-29 02:30:48

1

是啊,看看流量控制模塊,如步,鏈,或流〜,我想有這樣的事情在underscore.js太

+1

某事是'_.after' – Raynos 2011-05-13 16:30:14

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(":("); 
}); 
+6

它幾乎就像音樂它是如此美麗 – jcolebrand 2011-05-13 17:31:07

+0

是當node.js在單個進程中單線程運行時,這真的並行處理這些任務?或者是將任務推到事件隊列的末尾,稍後再處理,而當事件隊列沒有其他事情要做時? – 2011-05-16 20:19:50

+0

@AntKutschera沒有多線程。它只是說並行運行a-g(在不同的線程中由不同的進程處理異步),並且當任何配對完成時運行啓動該子進程的此函數 – Raynos 2011-05-16 20:27:53

9

你應該檢查步驟(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個完成時,請轉到下一步」,認真重新考慮您的設計。你真的需要這麼複雜的模式嗎? (也許你正在等待法定人數)。這種複雜的模式值得它自己的功能。
+0

是組任務是並行還是串行 – 2012-03-10 06:05:08

+0

對於這種異步有一些缺點,即如果你想調用一個實用程序方法,比如使用瀑布,你需要有一個包裝來處理錯誤。例如:collection.find.bind(...),您可以在鏈中使用函數綁定器...這非常有用,並且不適用於步驟。 – Tracker1 2013-07-25 17:21:52

0

nimble是另一個不錯的選擇。