【问题标题】:How to run a function only after multiple functions have been resolved?解决了多个功能后如何运行一个功能?
【发布时间】:2014-08-06 17:13:16
【问题描述】:

我有几个功能基本上准备了我的应用程序。 一旦这些准备功能完成,我想运行我的应用程序的主循环。

因此,虽然准备函数彼此异步,但主循环仅在所有准备函数都已解析后运行。

function prep1(x) { do something };
function prep2(x) { do something };
function prep3(x) { do something };

function mainLoop(x) { only start this function when all 3 prep ones are resolved };

我基本上是在学习 nodejs 中的控制流。我的问题是如何使用:

  1. 回调
  2. 事件
  3. promises(如果正在使用库,名称就足够了)

【问题讨论】:

标签: javascript node.js events callback


【解决方案1】:

在 Node.JS 中有一些不同的方式来进行控制流。 “传统”最先进的做法是使用回调,但这会导致我们称之为“回调地狱”或回旋镖效应。您还可以使用 promises库来处理回调地狱。在 Node.JS 中进行控制流(以及其他)的另一种方法是使用流。

有关不同控制流技术的示例,请参见下文。

使用回调的示例:

function prep1(x, cb) { do something; cb(); };
function prep2(x, cb) { do something; cb(); };
function prep3(x, cb) { do something; cb(); };

function mainLoop(x) { };

prep1(x, function () {
    prep2(y, function () {
        prep3(z, function () {
            mainLoop();
        });
    });
});

// could be shortened to
prep1(x, prep2(y, prep3(z, mainLoop)));

如您所见,这可能会导致一些非常丑陋的代码。另外,请注意,此代码不是并行运行,而是串行运行。如果您想在每个准备函数中都有某种异步操作的情况下并行运行它,请参见下文以了解这将如何工作。

使用并行回调的示例:

// cb() is called inside some async operation
function prep1(x, cb) { do async; cb(y); };
function prep2(x, cb) { do async; cb(y); };
function prep3(x, cb) { do async; cb(y); };

function mainLoop(x) {
    // x is an array of values given by the callback in prep1-3
};

var numCalls = 0, result = [];
function maybeCallMainLoop(data) {
   if (++numCalls !== 3) {
       // Accumulate all responses given by  argument y from prep1-3
       result.push(data);
       return;
   }
   // run main loop when all functions are called
   mainLoop(result);
}

prep1(x, maybeCallMainLoop);
prep2(y, maybeCallMainLoop);
prep3(z, maybeCallMainLoop);

这并不是最干净、最可靠的方法。更好的方法是下一个示例。


使用async library 是解决此问题的一种更简洁的方法。更具体地说,#parallel method

使用#parallel methodasync library 的示例

// Signature of the callback: callback(error, value)
function prep1(x, cb) { do something; cb(null, "value"); };
function prep2(x, cb) { do something; cb(null, "value"); };
function prep3(x, cb) { do something; cb(null, "value"); };

function mainLoop(error, x) { };

async.parallel([
    prep1,
    prep2,
    prep3
], mainLoop);

the async readme中查看更多信息


正如您所说,第三种方法是使用 Promise。您将从每个准备函数返回一个承诺,然后等待执行 mainLoop

您可以使用Q library,它是一个符合 A* 的承诺库。

通过Q library 使用承诺的示例

var Q = require('q');

// Signature of the callback: callback(error, value)
function prep1(x) { 
    // do something; 
    var deferred = Q.defer();
    deferred.resolve() // resolve promise
    return deferred.promise;
};
function prep2(x) { // same as prep1 };
function prep3(x) { // same as prep1 };

function mainLoop(result) { };

// Q.all returns a promise that is 
// resolved when all promises given are resolved
Q.all([
    prep1(x),
    prep2(y),
    prep3(z)
]).then(mainLoop);

documentation of Q.all


这是解决它的一些方法。我并没有真正看到使用事件以一种漂亮的方式解决这个问题的方法——但它看起来很像带有并行回调的示例。带有一个标志,指示已完成的准备功能的数量。

您可以看到,使用 async 或 Promise 之类的东西很容易成为最容易理解和易读的东西。如果您只想获得“瘦”的结果(cyclomatic complexity 较低的函数),我可能会建议使用async。如果您的准备工作说明了某些复杂性和/或结果如何以不同的方式使用,那么 Promise 将是最好的方法。

关于流的小结

在 Node.JS 中,你也有一个叫做 streams 的东西。以最基本的方式,流是可管理的事件发射器,具有背压(一种停止数据流的方式)功能。阅读Daddy, what's a stream? 中的一个简单类比。

使用流,您可以拥有一个源并将其通过不同的转换流进行管道传输,并以某种接收器或端点结束流。这就是串行而不是并行的方式,但是您可以使用流做更多的事情。 Streams 与 Promise 有一些共同点。

使用流的示例

function prep1(x) { return stream; };
function prep2(x) { return stream; };
function prep3(x) { return stream; };

function mainLoop(result) { };

// Some start. E.g fs.createReadStream("somefile")
someStartStream
   .pipe(prep1(x))
   .pipe(prep2(y))
   .pipe(prep3(z))
   .on('end', mainLoop);

同样,这是按顺序进行的,而不是并行的。这只是为了表明在 Node.JS 的控制流中你也可以使用流。

祝你好运!

【讨论】:

  • 谢谢。太棒了。我现在可以比较和对比您提供的 3 个代码示例。
  • 关于事件,我们可以使用状态标志吗?
  • 有一些方法可以使用事件来解决这个问题,但在我看来,它们并没有太大的意义或真的太有用了。如果你真的想要,我可以用一个可能的解决方案来更新我的答案——我不建议以任何方式使用它。
  • 没关系。我想学习实现这一结果的最佳方法。你的回答让我可以自己尝试所有 3 种方法。
  • 请注意,回调示例不是并行运行,而是顺序运行。
【解决方案2】:

您还可以使用名为 nimble 的模块。

npm install nimble --save

然后按如下方式构建您的代码:

var flow = require('nimble');

flow.series([
     function (callback) {
          function prep1(x) { do something };
          callback();
     },
     function (callback) {
          function prep2(x) { do something };
          callback();
     },
     function(callback) {
          function prep3(x) { do something };
          callback();
     },
     function(callback) {
          function mainLoop(x) { };
          callback();
     }
]);

【讨论】:

    猜你喜欢
    • 2023-01-24
    • 2010-12-28
    • 1970-01-01
    • 1970-01-01
    • 2016-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多