Node.js,异步模块,并发

Node.js, async module, concurrency

我是否最好避免一次使用多个异步模块功能实例?

我的代码有三个部分要按顺序执行。我正在使用

这样的代码
var async = require('async');
async.waterfall(
  [ function(cb) { part1(); },
    function(cb) { part2(); },
    function(cb) { part3(); }
  ],
  function(err, res) { console.log('Done'); }
);

part1() 也包含三个部分。其中第三个在前两个之后运行。我的想法是使用

function part1(err, res) {
  async.waterfall(
    [ function(cb) {
        async.parallel(
          [ function(cb) { part1a(); },
            function(cb) { part1b(); }
          ]
        );
      },
      function(cb) { part1c(); },
      function(cb) {
        if (err) return err('part 1 error')
        return res()
      }
    ]
  );
}

第 1a 部分和第 1b 部分是 运行,我使用了异步的三个功能:外部瀑布,以及第 1 部分中的瀑布和并行。我重组了 part1 以仅使用一个功能:瀑布,part1a 然后 part1b 然后 part1c 只是为了让它工作。

我没有取得成功。例如,我没有看到消息'Done'。当我重组 part1 时,我接到了 part1a 的电话,但没有接到 part1b 或任何其他电话。

也许我并不孤单。在 Optional callback not being called in the node.js async module's forEachOf method 中,@LuisDelgado 表示,"As a personal note, I have not had so far success in having async work nicely with a function that has an internal callback-async function itself."

中,@Robbie 和@dark_shadow 在 async.auto 中取得了一些成功。我想这将涉及通过将我的三个外层函数分解成它们的组件并从外层调用每个组件来扁平化我的代码。也许那是 "the JavaScript way"!

什么是对的?

提前致谢...

编辑:干得好,亚当·萨克斯。非常感谢!一个 follow-on ...

编辑:亚当和其他人提出了承诺。 Adam 喜欢 bluebird 并展示了一些有用的语法。我在蓝鸟方面遇到了麻烦,尽管它很吸引人。我在这里发布了一个 follow-on bluebird 问题。通过此编辑,我将删除 follow-on,将此问题(如标题所示)保留为异步问题。

感谢那些鼓励我使用 promises 的人!请参阅

异步问题仍然存在:我是否最好避免一次使用多个异步模块功能实例?

您可能想尝试使用 Promises for this sort of orchestration of asynchronous actions. Promises are now a native JS feature for nodejs and newer browsers. There are also a ton of libraries for Promises. One of the ones I use the most is bluebird,它有一些很棒的实用功能和额外的 Promise 糖。

对于您的示例,您将有类似的内容:

part1()
    .then(part2)
    .then(part3)
    .then(part4)
    .then(function() {
         console.log("Done!");
    });

function part1a() {
   return new Promise(function(resolve, reject){
     setTimeout(resolve, 1000);
   })
}

function part1() {
     return Promise.all([
         part1a(),
         part2b();
     ]);
}

我还建议在这种情况下使用 promises,因为它们更好——更少的代码和单一的执行链和错误处理。如果您想坚持使用异步,则需要在完成异步操作后调用传递给异步方法的回调。

var async = require('async');
async.waterfall(
  [ function(cb) { part1(cb); },
    function(cb) { part2(cb); },
    function(cb) { part3(cb); }
  ],
  function(err, res) { console.log('Done'); }
);

function part1(callbackFromAsync) {
  async.waterfall(
    [ function(waterfallCallback) {
        async.parallel(
          [ function(cb) { part1a(cb); },
            function(cb) { part1b(cb); }
          ], waterfallCallback // <- this needs to be called
        );
      },
      function(cb) { part1c(cb); },
      function(cb) {
          callbackFromAsync(); // <- this needs to be called!
      }
    ]
  );
}

实际上,每次异步操作完成时,都需要调用一个 cb 函数来表示其完成。想象一下 part2part3part1a 等是更简单的函数,看起来像这样:

function part(callback) {
  someAsyncOperation(function (err, result) {
    callback(err, result);
  });
}

您必须调用传递给该函数的回调来表示异步操作已完成。您使用回调传递错误(如果有)和结果(如果需要)备份函数堆栈。 callback 的调用是 async 知道你的异步函数完成的方式。