【问题标题】:NodeJS: How to handle a variable number of callbacks run in parallel and map their responses to requests?NodeJS:如何处理并行运行的可变数量的回调并将它们的响应映射到请求?
【发布时间】:2013-09-16 04:44:47
【问题描述】:

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

在我使用读取域的函数之前,一切都运行得很顺利。为此,aws-sdk 有两个函数:listDomainsdomainMetadata。 listDomains 返回一个 sdb 域名数组。 domainMetadata 将返回有关域的其他统计信息,但一次仅返回一个域的统计信息。结果中不包含域名。

我的脚本正在运行 listDomains 并在 JSON 响应中返回一个数组就好了。我想让我的 api readDomains 函数更加雄心勃勃,并让它在同一个 api 调用中返回所有域的元数据。毕竟,同时运行少量的 domainMetadata 调用是 node 的异步 io 应该发挥作用的地方。

问题是我不知道如何运行可变数量的调用,对所有调用使用相同的回调,将每个 domainMetadata 调用的结果与它的 domainName 匹配(因为它是异步的并且不能保证按请求的顺序返回)并告知所有元数据请求何时完成,以便我可以发送最终响应。将我的问题区域放入代码中:

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 函数至少应该通过一些黑魔法将元数据响应与请求匹配,但它会导致节点在 aws 同步库中爆炸,并出现错误“没有方法”makeRequest '"。

有没有什么办法让一切都得到:请求并行运行,请求与响应相匹配,并知道我何时收到了所有信息?

【问题讨论】:

  • 查看 promise 或 async.js
  • Q 和其他承诺连续运行,所以我失去了节点异步设置的巨大好处。我尝试了 async.map 没有运气。正如我所提到的,它导致 aws-sdk 出现错误。 async.parallel 需要使用静态函数调用编写,因此不适用于可变数量的调用。如果我错了,您能否在这种情况下提供一个可行的示例调用?
  • 您可以创建许多并行运行的 Promise,然后使用 Q.all 等待它们的结果。对于Q.allasync.parallel,您可以传递具有任意数量任务的数组。

标签: javascript node.js amazon-web-services express


【解决方案1】:

使用.bind(),您可以设置上下文或this 值,并为绑定函数提供前导默认参数。

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

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

  • domainResults 对象设置为receiveMetaData 回调的上下文
  • 将当前域名作为参数传递给回调

domainResults 对象用于:

  • 跟踪第一个请求中收到的名称数量
  • 跟踪 completedCount(在 metaData 请求的每个回调中递增)
  • 跟踪列表中的错误和成功响应
  • 提供完整的回调

完全未经测试的代码仅用于说明目的:

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();
    }
}

【讨论】:

  • 效果很好。作为奖励,它可以保持代码清洁并避免需要额外的库。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-14
  • 1970-01-01
  • 1970-01-01
  • 2016-10-02
相关资源
最近更新 更多