我在一个节点模块中有一个函数,它是一组三个嵌套的回调,并且工作得非常好,给了我所需的线性,因为每个嵌套的回调都依赖于前一个回调的数据。问题是第二个函数的回调需要冒泡并递归调用其父函数。它与外部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/
答案 0 :(得分: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
});
});
});
}),
};
答案 1 :(得分:0)
我认为你可以在包含pages
的闭包中打破biglistofresults
函数。
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);
});