如何在node.js中协调并行执行?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (47)

node.js的事件驱动编程模型使协调程序流程有点棘手。

简单的顺序执行变成了嵌套的回调,这很容易(尽管有点复杂的写下来)。

但是并行执行怎么样?假设你有三个任务A,B,C可以并行运行,当他们完成后,你想把他们的结果发送给任务D.

用叉/连接模型,这将是

  • 叉A
  • 叉B
  • 叉C
  • 加入A,B,C,运行D

我如何在node.js中编写它?有没有最佳做法?

提问于
用户回答回答于

因为它是单线程的,所以在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或承诺。只要考虑上面的答案,就可以很好地解释/说明诸如async.parallel的工作。

用户回答回答于

我相信现在“异步”模块提供了这种并行功能,并且与上面的fork函数大致相同。

扫码关注云+社区

领取腾讯云代金券