我正在尝试从API中获取一堆ID,然后形成一系列请求,这些请求将进一步调用API以获取某些参数。这些将是总计的,我希望输出结果作为JSON数组推送。
问题是REST调用是异步的,我已经发出了承诺但不确定何时将承诺解析回调用函数,其余的调用有时会花费一秒或2来回复。
我想知道在什么时候我可以解决这个承诺或如何知道何时计算总数?
路线
app.get("/sonar/:x_id",function(req,resp) {
getRestSonar(req.params.x_id).then(function (fromResolve) {
resp.send(fromResolve);
});
});
带有promise的函数,使其余的调用循环
var getRestSonar = function(requestX) {
return new Promise(function(resolve,reject) {
var unirest = require("unirest");
var reqx = unirest("GET", "http://sonarqubexxServer/api/projects");
var outputJson = {
table: []
};
reqx.end(function (res) {
if (res.error) throw new Error(res.error);
// console.log(res.body);
var result = res.body;
//var needle = req.params.csi_id;
var needle = requestX;
var TotalDuplicateLines = 0;
var TotalBugs = 0;
var TotalNcloc = 0;
var TotalCodeSmells = 0;
var TotalVulnerabilities = 0;
for (var i=0;i<result.length;i++) {
if (result[i].nm.indexOf(needle) !== -1) {
console.log(result[i].k);
var queryUrl = "http://sonarqubexxServer/api/resources?resource="+result[i].k+"&metrics=code_smells,bugs,vulnerabilities,ncloc,coverage,duplicated_lines&format=json"
console.log(queryUrl);
var subrequest = unirest("GET",queryUrl);
subrequest.end(function (resXX) {
if (resXX.error);
var resXXResult = resXX.body;
for (var i=0;i<resXXResult.length;i++) {
// var duplicateData = resXXResult[0].msr.filter(item => item.key == 'duplicated_lines');
resXXResult[i].msr.forEach(m => {
if (m.key === 'duplicated_lines') {
console.log('Duplicated Lines ' + m.val);
TotalDuplicateLines += m.val;
}
else if(m.key === 'bugs' ) {
console.log('Bugs ' + m.val);
TotalBugs += m.val;
}
else if(m.key === 'ncloc' ) {
console.log('Lines of Code ' + m.val);
TotalNcloc += m.val;
}
else if(m.key === 'code_smells' ) {
console.log('Code Smells ' + m.val);
TotalCodeSmells += m.val;
}
else if(m.key === 'vulnerabilities' ) {
console.log('Vulnerabilities ' + m.val);
TotalVulnerabilities += m.val;
outputJson.table.push({totduplines:TotalDuplicateLines},{totVul:TotalVulnerabilities});
}
});
console.log("Iam here with I :: " + i);
if (i === (resXXResult.length - 1)) {
//Should i resolve here makes no sense
console.log("Resolved the promise now..");
}
//The for ends here
}
// I see this is a bad place to resolve..
resolve(outputJson);
});
}
}
});
});
}
编辑:正如评论中所建议的那样,将呼叫拆分为更小的 部分
现在,我从api中调用apiratly创建一个数组,然后使用promises回调API?我如何通过循环来解决每个调用?
当我尝试循环时它总是解析request[0]
然后出现了承诺,我怎样才能创建一个promise数组并等待它们完成?
app.get("/sonar/:csi_id",function(req,resp) {
var collectiveResult = [];
getRestSonar(req.params.csi_id).then(function (fromResolve) {
return splitReqUrl(fromResolve);
}).then(function(fromSplitUrl) {
console.log("I am from split url ::::" + fromSplitUrl);
return getSubSonarProperties(fromSplitUrl);
}).then(function(fromsubSonar) {
collectiveResult.push(fromsubSonar);
console.log("+++++++++++++++++++++++++++");
console.log(fromsubSonar);
resp.send(collectiveResult);
});
});
var getSubSonarProperties = function(getUrl) {
return new Promise(function(resolve,reject) {
var getSubRest = require("unirest");
console.log("Attempting to GET " + getUrl);
var req = getSubRest("GET",getUrl);
var outputJson = {
table: []
}
var TotalDuplicateLines = 0;
var TotalBugs = 0;
var TotalNcloc = 0;
var TotalCodeSmells = 0;
var TotalVulnerabilities = 0;
req.end(function (res) {
if (res.error);
var resXXResult = res.body;
resolve(resXXResult);
});
});
}
var splitReqUrl = function(request) {
return new Promise(function(resolve,reject) {
resolve(request[1]);
//for(var i=0; i< request.length; i++) {
// resolve(request[i]);
//}
});
}
var getRestSonar = function(requestX) {
return new Promise(function(resolve,reject) {
var unirest = require("unirest");
var reqx = unirest("GET", "http://sonarqubexxx/api/projects");
var outputJson = {
table: []
};
reqx.end(function (res) {
if (res.error) throw new Error(res.error);
// console.log(res.body);
var result = res.body;
//var needle = req.params.csi_id;
var needle = requestX;
var queryArray = [];
for (var i=0;i<result.length;i++) {
if (result[i].nm.indexOf(needle) !== -1) {
console.log(result[i].k);
var queryUrl = "http://sonarxxx/api/resources?resource="+result[i].k+"&metrics=code_smells,bugs,vulnerabilities,ncloc,coverage,duplicated_lines&format=json"
//console.log(queryUrl);
queryArray.push(queryUrl);
}
if (i === (result.length - 1)) {
resolve(queryArray);
}
}
});
});
}
答案 0 :(得分:1)
首先,您的解决方案的问题在于您尝试在一个大new Promise(...)
创建者中创建所有内容。
即使您使用.then(...)
方法将Promise设为链接,也可以设法使其成为it's still a common anti-pattern。
正如Roamer-1888所指出的,有一个unirest
的分支可以直接处理Promise而不需要像你的例子那样要求回调,但是让我们坚持使用{{1}的版本这里。
所以你需要做的是创建一个Promise链来处理代码的不同步骤并将结果传递给链。
您的步骤似乎是:
unirest
输入过滤结果。requestX
对象。基本上唯一的异步步骤是1和3,但是可以添加第三步来构建outputJson
并将其传递到下游。
所以,让我们从第一步开始。
在Promise链的第一个链接中,我们需要通过您的第一次outputJson
调用来检索初始结果:
unirest
在这个例子中,我已经检查了响应是否包含错误并在这种情况下解除了拒绝,否则我解决了主体的承诺(我们需要的数据)。
如果请求失败,我们上面创建的Promise将抛出一个错误,如果一切正常,它将在响应主体的下游。
现在我们可以继续使用new Promise((resolve, reject) => {
unirest("GET", "http://sonarqubexxServer/api/projects")
.end((res) => {
if (res.error) {
reject(res.error);
} else {
resolve(res.body);
}
});
})
方法充分发挥Promise的潜力:
.then(...)
在这一步中,我使用了一些new Promise((resolve, reject) => {
unirest("GET", "http://sonarqubexxServer/api/projects")
.end((res) => {
if (res.error) {
reject(res.error);
} else {
resolve(res.body);
}
});
}).then((results) => {
results = results.filter((result) => {
return result.nm.indexOf(request) != -1;
});
return Promise.all(results.map((result) => {
return new Promise((resolve, reject) => {
var queryUrl = "http://sonarqubexxServer/api/resources?resource=" + result.k + "&metrics=code_smells,bugs,vulnerabilities,ncloc,coverage,duplicated_lines&format=json"
unirest("GET", queryUrl)
.end((res) => {
if (res.error) {
reject(res.error);
} else {
resolve(res.body);
}
});
})
}))
})
方法来使代码更清晰,并Array
一起处理多个承诺。
Promise.all
是一种迭代数组并检查每个项目的方法,如果它应该保留在过滤后的输出中。因此,在您的情况下,我们只想保留Array.filter
。
result.nm.indexOf(request) != -1
是一种迭代数组并将每个项目转换为其他项目的方法。基本上,您提供的函数将每个项目作为输入,将其转换为其他项目,然后将此新值替换为输出数组中的旧值。
最后Array.map
接受一个Promises数组并返回一个Promise本身。当所有给定的Promise解析时,返回的Promise将解析,并将向下游传递一个数组,其中的项目是每个Promise的结果。
因此,通过编写Promise.all
,我们将结果数组中的每个结果转换为一个Promise,它执行特定于结果的调用并将其放入Promises的输出数组中,该数组被提供给Promise.all(results.map((results) => { return new Promise(...) }))
,因此它们得到马上执行。
现在,Promise链输出Promise.all
的结果,这是每个Promise的所有结果的数组,这是每个子调用的结果。
然后我们可以简单地获取下游数据并使用嵌套迭代来构建要传递到下游的Promise.all
:
outputJSON
如果您在new Promise((resolve, reject) => {
unirest("GET", "http://sonarqubexxServer/api/projects")
.end((res) => {
if (res.error) {
reject(res.error);
} else {
resolve(res.body);
}
});
}).then((results) => {
results = results.filter((result) => {
return result.nm.indexOf(request) != -1;
});
return Promise.all(results.map((result) => {
return new Promise((resolve, reject) => {
var queryUrl = "http://sonarqubexxServer/api/resources?resource=" + result.k + "&metrics=code_smells,bugs,vulnerabilities,ncloc,coverage,duplicated_lines&format=json"
unirest("GET", queryUrl)
.end((res) => {
if (res.error) {
reject(res.error);
} else {
resolve(res.body);
}
});
})
}))
}).then((allResults) => {
var TotalDuplicateLines = 0;
var TotalBugs = 0;
var TotalNcloc = 0;
var TotalCodeSmells = 0;
var TotalVulnerabilities = 0;
var outputJson = {
table: []
};
for (var i = 0; i < allResults; i++) {
for (var j = 0; j < allResults[i].length; j++) {
allResults[i][j].msr.forEach(m => {
if (m.key === 'duplicated_lines') {
TotalDuplicateLines += m.val;
}
else if (m.key === 'bugs') {
TotalBugs += m.val;
}
else if (m.key === 'ncloc') {
TotalNcloc += m.val;
}
else if (m.key === 'code_smells') {
TotalCodeSmells += m.val;
}
else if (m.key === 'vulnerabilities') {
TotalVulnerabilities += m.val;
outputJson.table.push({ totduplines: TotalDuplicateLines }, { totVul: TotalVulnerabilities });
}
});
}
}
return outputJson;
})
函数中返回此长Promise链,则可以编写getRestSonar(request)