简体   繁体   English

NodeJS REST API等待响应

[英]NodeJS REST API wait for response

I am trying to get a bunch of ID's from an API and then form a sequence of requests which would make further calls to an API to fetch some parameters. 我试图从API获取一堆ID,然后形成一系列请求,这些请求将进一步调用API以获取一些参数。 These would be totaled and i expect the output results to be pushed as JSON array. 这些将总计,我希望输出结果将作为JSON数组推送。

The problem is REST call is async and i've put a promise but not sure when to resolve the promise back to the calling function, the rest call some times take a second or 2 to respond back. 问题是REST调用是异步的,我已经做出了承诺,但是不确定何时将承诺解析回调用函数,其余的调用有时需要一到两秒钟才能回复。

I would like know at what point can i resolve the promise or how to know when the totals have been computed ? 我想知道在什么时候可以兑现承诺,或者如何知道何时计算出总额?

The Route 路线

app.get("/sonar/:x_id",function(req,resp) { 

getRestSonar(req.params.x_id).then(function (fromResolve) {
  resp.send(fromResolve);
});


});

The function with promise which makes the rest call loops 带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);


                         });


            }
      }



    });


});

}

EDIT : As suggested in the comments, split the calls into smaller sections 编辑:如注释中所建议,将调用分成较小的部分

Now, i fetch the api calls seperatly create an array out of it, then use promises to call back to the API ? 现在,我获取api调用,分别从中创建一个数组,然后使用promises回调到API? how do i resolve each call by looping over it ? 我如何通过循环解决每个呼叫? When i try to loop it always resolves request[0] and then comes out of the promise, how can i create a promise array and wait for them to complete ? 当我尝试循环时,它始终会解析request[0] ,然后脱离promise,我如何创建一个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);
                  }
            }
    });
});
}

Problem 问题

First of all the problem with your solution is that you're trying to make everything inside a single big new Promise(...) creator. 首先,您的解决方案存在的问题是,您试图将所有内容都放入一个new Promise(...)大型创建器中。

Even if you manage to make that work it's still a common anti-pattern as Promises are made to be chained using the .then(...) method. 即使您设法完成该工作, 它仍然是一种常见的反模式,因为使用.then(...)方法将Promises 链接 .then(...)

As pointed out by Roamer-1888 there oughta be a fork of unirest that handles Promises directly instead of requiring callbacks as in your example, but let's stick with your version of unirest here. 正如Roamer-1888所指出的,应该有一个unirest的分叉, unirest可以直接处理Promises,而不是像您的示例中那样要求回调,但是在这里unirest的版本。

Solution

So what you need to be doing is create a Promise chain to handle the different steps of your code and pass the results down the chain. 因此,您需要做的是创建一个Promise链来处理代码的不同步骤,并将结果传递给链。

Your steps seem to be: 您的步骤似乎是:

  1. Make the first call to retrieve initial results. 进行第一次调用以检索初始结果。
  2. Filter the results based on the requestX input. 根据requestX输入过滤结果。
  3. For each item left, make several calls to obtain more data. 对于剩下的每个项目,进行几次调用以获取更多数据。
  4. Put everything back into an outputJson object. 将所有内容放回outputJson对象。

Basically the only async steps are 1 and 3, but it might be ok to add a third step to build your outputJson and pass it downstream. 基本上,唯一的异步步骤是1和3,但是可以添加第三步来构建您的outputJson并将其传递给下游,这是可以的。

So let's start with the first step. 因此,让我们从第一步开始。

1. Make the first call 1.打个电话

In the first link of the Promise chain we need to retrieve the initial results with your first unirest call: 在Promise链的第一个链接中,我们需要使用您的第一个unirest调用来检索初始结果:

new Promise((resolve, reject) => {
    unirest("GET", "http://sonarqubexxServer/api/projects")
        .end((res) => {
            if (res.error) {
                reject(res.error);
            } else {
                resolve(res.body);
            }
        });
})

See in this example I already checked if the response contains an error and fired a rejection in that case, otherwise I resolve the promise with the body (the data we need). 在此示例中看到,我已经检查了响应是否包含错误并在这种情况下触发了拒绝,否则我将使用主体(我们需要的数据)来解决promise。

The Promise we created above will throw an error if the request fails, and will downstream the body of the response if everything goes fine. 如果请求失败,我们上面创建的Promise将引发错误,如果一切正常,将在响应的主体下游。

2. Filtering and Sub-calls 2.过滤和子呼叫

Now then we can go ahead and use the full potential of Promises with the .then(...) method: 现在,我们可以继续使用.then(...)方法来充分利用Promises的潜力:

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

In this step I used some Array methods to make the code cleaner and Promise.all to handle several promises together. 在这一步中,我使用了一些Array方法来使代码更干净, Promise.all可以一起处理多个promise。

Array.filter is a method which iterates an array and checks for each item if it should be kept in the filtered output or not. Array.filter是一种迭代数组并检查每个项目是否应保留在过滤输出中的方法。 So, in your case, we want to keep only those items where result.nm.indexOf(request) != -1 . 因此,在您的情况下,我们只想保留result.nm.indexOf(request) != -1那些项目。

Array.map is a method which iterates an array and converts each item to something else. Array.map是一种迭代数组并将每个项目转换为其他内容的方法。 Basically the function you provide takes each item as input, converts it to something else and then replaces this new value to the old one in the output array. 基本上,您提供的函数将每个项目作为输入,将其转换为其他内容,然后将此新值替换为输出数组中的旧值。

Finally Promise.all accepts an array of Promises and returns a Promise itself. 最后, Promise.all接受一个Promises数组并返回Promise本身。 This returned Promise will resolve when all the given Promises resolve and will pass downstream an array which items are the results of each single Promise. 当所有给定的Promise都解析后,此返回的Promise将解析,并将向下游传递哪些项是每个单个Promise的结果。

So by writing Promise.all(results.map((results) => { return new Promise(...) })) we convert each result in the results array into a Promise that executes the result-specific call and put it into the output array of Promises which is fed to Promise.all so they get executed at once. 因此,通过编写Promise.all(results.map((results) => { return new Promise(...) }))我们将结果数组中的每个结果转换为执行特定于结果的调用的Promise,并将其放入Promise.all的Promises输出数组,因此它们可以立即执行。

3. Build the outputJSON 3.构建outputJSON

Now the Promise chain outputs the result of Promise.all which is an array of all the results of each Promise, which are the results of each sub-call. 现在,Promise链输出Promise.all的结果,该结果是每个Promise的所有结果的数组,这些结果是每个子调用的结果。

We can then simply take the downstream data and use your nested iterations to build the outputJSON to be passed downstream: 然后,我们可以简单地获取下游数据,并使用嵌套的迭代来构建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;
})

If your return this long Promise chain in your getRestSonar(request) function, then you could write getRestSonar(request).then((outputJson) => { ... do something with your outputJson ... }) 如果您在getRestSonar(request)函数中返回了这条较长的Promise链,则可以编写getRestSonar(request).then((outputJson) => { ... do something with your outputJson ... })

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM