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

简单的顺序执行会转化为嵌套回调,这很容易(尽管写下来有点复杂).

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

对于fork/join模型,这将是

  • fork
  • fork B
  • fork C
  • 加入A、B、C、D

我该如何在node中写呢.js?有什么最佳实践或食谱吗?我是不是每次都得打hand-roll a solution分,还是有图书馆有帮手?

推荐答案

在 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);

在上面的示例中,我们假设异步函数和回调函数不需要参数,从而使代码保持简单.当然,您可以修改代码,将参数传递给异步函数,让回调函数累积结果并将其传递给共享的回调函数.


其他答案:

实际上,尽管如此,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的库.我愿意相信异步.js的灵感来源于此,但我没有任何证据.无论如何如果你今天想这么做,看看async.或promise .只要考虑上面的答案,就可以很好地解释和说明事物是什么样子的.并行工作.

为了完整性起见,以下是使用async.parallel的方法:

var async = require('async');

async.parallel([A,B,C],D);

请注意,async.parallel的工作原理与我们上面实现的fork函数完全相同.主要区别在于,它将错误作为第一个参数传递给D,并根据 node 将回调作为第二个参数.js公约.

使用promise ,我们将写如下:

// Assuming A, B & C return a promise instead of accepting a callback

Promise.all([A,B,C]).then(D);

Node.js相关问答推荐

NX无法使用缓存运行根级脚本

仅当所需文档是最后一个文档时才更新MongoDB,否则插入

MongooseError[MissingSchemaError]:尚未为模型注册架构

Node-Red Tasmota 错误:连接 ECONNREFUSED 192.168.77.21:1883

如果我在 tsx 文件中使用了use client,ssr 会如何发生?

dayjs的isSameOrAfter方法未按预期工作

如何在 Firestore 函数上使用类型模型来获取字段值

我正在try 在公共目录中使用 Express.js 项目部署 Angular 静态构建

如何使用对象中的常量值验证字符串字段?

为什么 FastAPI 需要 Web 服务器(即 Nginx)而不是 Express API?

Axios 响应循环通过函数只返回第一个映射对象的结果

使用 node/express/Multer,您如何首先判断参数?

discordjs如何添加所有意图/权限

提供静态文件到底是什么意思?

Nodejs-console.error vs util.debug

判断一个数组中的每个元素是否都在第二个数组中

chart.js 无法创建图表:无法从给定项目获取上下文

需要 node-gyp 的 npm install 在 Windows 上失败

Node.js 与 .net 中的异步/等待

Npm postinstall 仅用于开发