node.js的事件驱动编程模型使得协调程序流变得有些棘手。
简单的顺序执行被转换为嵌套回调,这很容易(尽管写起来有点复杂)。
但是并行执行呢?假设您有三个可以并行运行的任务A、B、C,当它们完成后,您希望将它们的结果发送到任务D。
对于fork/join模型,这将是
<代码>F29
我如何在node.js中编写它?有什么最佳实践或食谱吗?我是不是每次都要用hand-roll a solution,或者有没有带帮助器的库?
发布于 2011-01-08 09:54:30
在node.js中没有什么是真正并行的,因为它是单线程的。但是,可以安排多个事件并以您无法预先确定的顺序运行。而像数据库访问这样的一些事情实际上是“并行的”,因为数据库查询本身是在单独的线程中运行的,但在完成后又被重新集成到事件流中。
那么,如何在多个事件处理程序上调度回调呢?这是浏览器端javascript动画中常用的一种技术:使用变量来跟踪完成。
这听起来像是一个黑客,它确实是,而且它听起来可能很混乱,留下一堆全局变量来做跟踪,如果用一种较小的语言就会是这样。但在javascript中,我们可以使用闭包:
function fork (async_calls, shared_callback) {
var counter = async_calls.length;
var callback = function () {
counter --;
if (counter == 0) {
shared_callback()
}
}
for (var i=0;i<async_calls.length;i++) {
async_calls[i](callback);
}
}
// usage:
fork([A,B,C],D);
在上面的示例中,我们通过假设异步和回调函数不需要参数来保持代码的简单性。当然,您可以修改代码以将参数传递给异步函数,并让回调函数累积结果并将其传递给shared_callback函数。
其他答案:
实际上,即使是这样,fork()
函数已经可以使用闭包将参数传递给异步函数:
fork([
function(callback){ A(1,2,callback) },
function(callback){ B(1,callback) },
function(callback){ C(1,2,callback) }
],D);
剩下的唯一要做的事情就是将A、B、C的结果累加,并将它们传递给D。
更多额外的答案:
我无法抗拒。吃早餐的时候一直在想这个问题。下面是一个累积结果的fork()
实现(通常作为参数传递给回调函数):
function fork (async_calls, shared_callback) {
var counter = async_calls.length;
var all_results = [];
function makeCallback (index) {
return function () {
counter --;
var results = [];
// we use the arguments object here because some callbacks
// in Node pass in multiple arguments as result.
for (var i=0;i<arguments.length;i++) {
results.push(arguments[i]);
}
all_results[index] = results;
if (counter == 0) {
shared_callback(all_results);
}
}
}
for (var i=0;i<async_calls.length;i++) {
async_calls[i](makeCallback(i));
}
}
这已经很简单了。这使得fork()
具有相当的通用性,并可用于同步多个非同构事件。
Node.js中的用法示例:
// Read 3 files in parallel and process them together:
function A (c){ fs.readFile('file1',c) };
function B (c){ fs.readFile('file2',c) };
function C (c){ fs.readFile('file3',c) };
function D (result) {
file1data = result[0][1];
file2data = result[1][1];
file3data = result[2][1];
// process the files together here
}
fork([A,B,C],D);
更新
这段代码是在诸如async.js或各种基于promise的库出现之前编写的。我愿意相信async.js是受此启发的,但我没有任何证据。不管怎样..。如果你现在就打算这么做,可以看看async.js或promises。只需考虑上面的答案,就可以很好地解释/说明像async.parallel这样的东西是如何工作的。
为了完整起见,下面是如何使用async.parallel
完成此操作的
var async = require('async');
async.parallel([A,B,C],D);
请注意,async.parallel
的工作方式与我们上面实现的fork
函数完全相同。主要区别在于,根据node.js约定,它将错误作为第一个参数传递给D
,并将回调作为第二个参数传递。
使用promises,我们可以这样写:
// Assuming A, B & C return a promise instead of accepting a callback
Promise.all([A,B,C]).then(D);
发布于 2011-09-24 13:23:11
我相信"async“模块现在提供了这种并行功能,并且与上面的fork函数大致相同。
发布于 2012-02-20 12:47:09
将异步调用连接在一起,类似于
pthread_join
对线程的工作方式。
自述文件展示了一些使用freestyle或使用Promise模式的future子模块的很好的例子。文档中的示例:
var Join = require('join')
, join = Join()
, callbackA = join.add()
, callbackB = join.add()
, callbackC = join.add();
function abcComplete(aArgs, bArgs, cArgs) {
console.log(aArgs[1] + bArgs[1] + cArgs[1]);
}
setTimeout(function () {
callbackA(null, 'Hello');
}, 300);
setTimeout(function () {
callbackB(null, 'World');
}, 500);
setTimeout(function () {
callbackC(null, '!');
}, 400);
// this must be called after all
join.when(abcComplete);
https://stackoverflow.com/questions/4631774
复制相似问题