【问题标题】:Recursion inside a callback回调中的递归
【发布时间】:2013-01-14 00:10:05
【问题描述】:

我在一个节点模块中有一个函数,它是一组三个嵌套回调,它工作得很好,给了我所需的线性度,因为每个嵌套回调都依赖于来自前一个回调的数据。问题是第二个函数的回调需要冒泡并递归调用其父函数。它与外部 API 通信。这是实际代码,变量重命名以混淆我的超级顶级 sekrit 业务逻辑:

exports.account_usage = function (req, res) {

  var domainID = req.body.domains,
    startDate = req.body.date_start,
    endDate = req.body.date_end,
    accountItems = {},
    usage = {},
    domainStats = {},
    page = 0;
  //req.cs is a module that communicates with an external API to gather usage data
  req.cs.exec("listAccountItems", {
    "listall": "true",
      "domainid": domainID
  },

  function (error, result) {
    accountItems = result.item;
    console.log("listAccountItems callback");

    //Get Usage Records

    req.cs.exec("listUsageRecords", {
      "startdate": startDate,
        "enddate": endDate,
        "domainid": domainID,
        "page": page,
        "pagesize": 2000 //try not to DDOS the server. only fetch 2000 records at a time
    }, function (error, result) {
      usage = req._.extend(usage, result.usagerecord); //this is underscore
      console.log("Usage Records: " + usage.length);
      page++;
      //right here, if result.length === 2000, I need to call
      // listUsageRecords until result.length < 2000

      //got list of  Usage,
      //now process usage here

      //filter usage item 1
      var bytesrecords1 = req._.filter(usage, function (usage) {
        return (usage.usagetype === 4);
      });
      //sum
      var bytes1 = req._.reduce(bytesrecords1, function (memo, record) {
        return memo + parseInt(record.rawusage, 10);
      }, 0);
      console.log("Bytes1: " + bytes1);
      domainStats.bytes1 = (((bytes1 / 1000) / 1000) / 1000).toFixed(4);

      //filter usage item 2
      var bytesrecords2 = req._.filter(usage, function (usage) {
        return (usage.usagetype === 5);
      });
      //sum
      var bytes2 = req._.reduce(bytesrecords2, function (memo, record) {
        return memo + parseInt(record.rawusage, 10);
      }, 0);
      console.log("Bytes2: " + bytes2);
      domainStats.bytes2 = (((bytes2 / 1000) / 1000) / 1000).toFixed(4);

      req._.each(accountItems, function (account) {
        //get runnning hours
        var recs = req._.filter(usage, function (usage) {
          return (usage.accountid === account.id && usage.usagetype === 1);
        });
        account.usage = req._.reduce(recs, function (memo, record) {
          return memo + parseInt(record.rawusage, 10);
        }, 0);

        //filter all the recs for each usage type, 1-14
        console.log("Account Usage: " + account.usage);
        console.log("Account ID: " + account.name);
      });


      console.log("ready to render");
      res.render('usage', {
        "title": "Usage Report",
        "domain": domainStats
      });


    });
  });


};

实际代码也在这个小提琴中:[http://jsfiddle.net/3wTQA/1/][1] 我一直使用谷歌,直到我的手指流血,我不知道如何阻止内部回调继续而不是递归。 API 需要来自外部 API 的分页,以防止在需要获取大型数据集的情况下远程系统上的 DDOS。

编辑:这是经过注释和清理的实际代码,以及我尝试提取的一些数据示例:http://jsfiddle.net/3wTQA/1/

【问题讨论】:

    标签: javascript node.js asynchronous callback


    【解决方案1】:

    好的,我搞定了。我使用 async.whilst 函数留在回调中,直到获取所有数据。我是这样做的:

    exports.account_usage = function (req, res) {
    
      var domainID = req.body.domains,
        startDate = req.body.date_start,
        endDate = req.body.date_end,
        accountItems = {},
        usage = {},
        domainStats = {},
        page = 0;
      //req.cs is a module that communicates with an external API to gather usage data
      req.cs.exec("listAccountItems", {
        "listall": "true",
          "domainid": domainID
      },
    
      function (error, result) {
        accountItems = result.item;
        console.log("listAccountItems callback");
    
        //Get Usage Records
    async.whilst(
    function(){return count === pagesize},
    function (callback){
        req.cs.exec("listUsageRecords", {
          "startdate": startDate,
            "enddate": endDate,
            "domainid": domainID,
            "page": page,
            "pagesize": 2000 //try not to DDOS the server. only fetch 2000 records at a time
        }, function (error, result) {
           usage.usagerecord = usage.usagerecord.concat(result.usagerecord);
           count = result.usagerecord.length;
    
          console.log("Usage Records: " + usage.length);
          page++;
          callback();
          //now process usage here
    },
    function (err) {
          //filter usage item 1
          var bytesrecords1 = req._.filter(usage, function (usage) {
            return (usage.usagetype === 4);
          });
          //sum
          var bytes1 = req._.reduce(bytesrecords1, function (memo, record) {
            return memo + parseInt(record.rawusage, 10);
          }, 0);
          console.log("Bytes1: " + bytes1);
          domainStats.bytes1 = (((bytes1 / 1000) / 1000) / 1000).toFixed(4);
    
          //filter usage item 2
          var bytesrecords2 = req._.filter(usage, function (usage) {
            return (usage.usagetype === 5);
          });
          //sum
          var bytes2 = req._.reduce(bytesrecords2, function (memo, record) {
            return memo + parseInt(record.rawusage, 10);
          }, 0);
          console.log("Bytes2: " + bytes2);
          domainStats.bytes2 = (((bytes2 / 1000) / 1000) / 1000).toFixed(4);
    
          req._.each(accountItems, function (account) {
            //get runnning hours
            var recs = req._.filter(usage, function (usage) {
              return (usage.accountid === account.id && usage.usagetype === 1);
            });
            account.usage = req._.reduce(recs, function (memo, record) {
              return memo + parseInt(record.rawusage, 10);
            }, 0);
    
            //filter all the recs for each usage type, 1-14
            console.log("Account Usage: " + account.usage);
            console.log("Account ID: " + account.name);
          });
    
    
          console.log("ready to render");
          res.render('usage', {
            "title": "Usage Report",
            "domain": domainStats
          });
    
    
        });
      });
    }),
    
    };
    

    【讨论】:

      【解决方案2】:

      我认为您可以在包含biglistofresults 的闭包中拆分pages 函数。

      biglistofresults = {};
      function pages(id, page) {
          page = page || 0;
          module.exec("anotherAPIcall", {"id": id, "page": page }, 
              function (error, result) {
                  if (result.length === 2000) { //there is probably more data
                      biglistofresults = _.extend(biglistofresults, result);
                      pages(id, page + 1);
                  }
          );
      }
      module.exec("externalAPIcall", {"listall": "true", "domainid": domainID}, 
          function (error, result) {
              _.map(result, pages);
      });
      

      【讨论】:

      • 让我尝试将 pages 函数放在 _.map 中,这是我没有尝试过的一件事。
      • 这并没有真正起作用,因为 map 不是正确的功能; map 将结果的 key:values 作为参数传递给页面,这不是页面中的 api 调用需要发挥其魔力的信息。我将继续插入回调外部的函数,但是我之前对此的尝试回调保持正常运行,并且外部函数开始在递归调用中永远循环
      • 从 jsfiddle 看来,您似乎不需要 listAccountItems API 调用。也许这将有助于澄清您正在尝试做的业务逻辑。
      • 好的,jsfiddle.net/3wTQA/1 现在是整个函数,通过重命名所有变量进行了一定程度的清理,但功能与真正的函数完全相同
      猜你喜欢
      • 1970-01-01
      • 2017-02-05
      • 1970-01-01
      • 1970-01-01
      • 2019-04-03
      • 2015-02-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多