我有几个基本上准备我的应用程序的功能。 一旦这些准备功能完成,我想运行我的应用程序的主循环。
因此,虽然准备功能彼此异步,但主循环仅在所有准备功能都已解决后运行。
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中学习控制流。我的问题是如何通过以下方式实现我的需求:
答案 0 :(得分:2)
在Node.JS中有一些不同的控制流方式。 "传统"这样做的最直接的方法是使用回调,但这会导致我们称之为"回调地狱"或回旋镖效应。您还可以使用承诺或库来处理回调地狱。在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
的async 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);
中查看更多信息
第三种方法是使用promises,就像你说的那样。您将从每个准备函数返回一个承诺,而不是等待mainLoop
。
您可以使用Q library这是一个符合A *的承诺库。
的承诺的示例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);
这是解决问题的一些方法。我并没有真正看到使用事件以一种漂亮的方式解决这个问题的方法 - 但它看起来很像并行回调的例子。带有一个标志,指示已完成的准备功能的数量。
你可以看到使用async或promises这样的东西很容易理解,也很容易阅读。如果你只想得到"瘦"的结果。 (功能低cyclomatic complexity),我可能建议使用async。如果您的准备工作以不同方式使用某些复杂性和/或结果,那么承诺将是最好的方法。
关于流的小结尾说明
在Node.JS中,您还有一些名为streams的内容。以最基本的方式,流是具有背压(一种停止数据流的方式)功能的可管理事件发射器。在Daddy, what's a stream?中阅读一个简单的类比。
使用流,您可以拥有源并通过不同的转换流管道它,并使用某种接收器或端点结束流。这是一个系列而不是并行,但你可以用流做更多的事情。 Streams与promises有一些共性。
使用流的示例
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的控制流中你也可以使用流。
祝你好运!
答案 1 :(得分:1)
您还可以使用名为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();
}
]);