NodeJS:如何处理并行运行的可变数量的callback,并将它们的响应映射到请求?

作为一个自学更多的关于node js的练习,我开始使用aws-sdk为SimpleDB(sdb)创build一个基本的CRUD REST服务器。

一切都运行顺利,直到我得到一个读取域的function。 aws-sdk有两个function: listDomains和domainMetadata 。 listDomains返回一个sdb域名的数组。 domainMetadata将返回有关域的其他统计信息,但一次只返回一个域的统计信息。 它不包含结果中的域名。

我的脚本运行listDomains并在JSON响应中返回一个数组就好了。 我想让我的api readDomains函数更加雄心勃勃,并让它在同一个API调用中返回所有域的元数据。 毕竟,同时运行一些domainMetadata调用是节点的asynchronousio应该发光的地方。

问题是我无法弄清楚如何运行可变数量的调用,使用相同的callback所有这些,将每个domainMetadata调用的结果匹配到它的domainName(因为它是asynchronous的,他们不保证返回请求的顺序),并告诉所有的元数据请求何时完成,以便我可以发送我的最终答复。 把代码放在我的问题领域是:

domain.receiveDomainList = function(err, data){ var domainList = []; for(var i=0; i<data.DomainNames.length; i++){ sdb.domainMetaData({"DomainName":data.DomainNames[i]},domain.receiveMetadata); // alternatively: domainList.push({"DomainName":data.DomainNames[i]}); } // alternatively: // async.map(domainList, sdb.domainMetadata, domain.receiveMetadata) console.log(domainList); } domain.receiveMetadata = function (err, data){ // I figure I can stash the results one at a time in an array in the // parent scope but... // How can I tell when all of the results have been received? // Since the domainname used for the original call is not returned with // the results how do I tell what result matches what request? } 

根据我对async的自述文件的阅读,map函数应该至less通过一些黑魔法来匹配元数据响应和请求,但是它会导致节点在aws同步库中popup一个错误“has no method'makeRequest'”。

有没有什么办法可以做到这一点:请求并行运行,请求与响应匹配,并知道什么时候我收到了一切?

使用.bind(),你可以设置上下文或this值,以及为绑定函数提供主要的默认参数。

下面的示例代码纯粹是为了展示如何使用.bind()向响应callback中添加额外的上下文。

在下面的代码中, .bind用于:

  • 设置一个domainResults对象作为receiveMetaDatacallback的上下文
  • 将当前域名作为parameter passing给callback

domainResults对象用于:

  • 跟踪第一个请求中收到的姓名的数量
  • 跟踪completedCount(从元数据请求的每个callback中递增)
  • 跟踪列表中的错误和成功响应
  • 提供完整的callback

完全未经testing的代码仅供说明之用:

 domain.receiveDomainList = function(err, data) { // Assuming err is falsey var domainResults = { nameCount: data.DomainNames.length, completeCount: 0, list: {}, complete: function() { console.log(this.list); } }; for (var i = 0; i < data.DomainNames.length; i++) { sdb.domainMetaData({ "DomainName": data.DomainNames[i] }, domain.receiveMetadata.bind(domainResults, data.DomainNames[i])); } } domain.receiveMetadata = function(domainName, err, data) { // Because of .bind, this === domainResults this.completeCount++; this.list[domainName] = data || {error: err}; if(this.completeCount === this.nameCount) { this.complete(); } }