-1

In my project I've requirement to make a rest API call and fetch a JSON array in response.

Each array element will contain a item 'key' (project key). Response example is here.

Now I want to loop over this array, fetch project key, then a make another REST call to fetch details of each project.

I'm using below code to do this, but final output is not coming as expected.

var myOutput = [];
$.when (
    $.getJSON(projectListURL, function (data) {
        var compArr = data.components;
        for (i=0 ; i<compArr.length ; i++) {
            var projectKey = compArr[i].key;
            var projectName = compArr[i].name;

            var myURL = "http://myserver.com/projectdetails?projectkey=" + projectKey;
            $.getJSON(myURL, function (data) {

            }).then (function (data){
                console.log("myURL:" + myURL);
                console.log("name:" + projectName);
                var item = {};
                item["name"] = projectName;
                item["status"] = data.projectStatus.status;
                console.log(item);
                myOutput.push(item);
            });
        }
    })
).done (function () {
    console.log(myOutput);
});

I want myOutput to be something like:

[
    {name: "BaseProj", status: "OK"},
    {name: "Sudoku Project", status: "ERROR"},
    {name: "My Project", status: "WARN"}
]

But I'm getting:

[
    {name: "My Project", status: "OK"},
    {name: "My Project", status: "ERROR"},
    {name: "My Project", status: "WARN"}
]

Please suggest what should be the correct approach?

Thank You

reiley
  • 3,759
  • 12
  • 58
  • 114
  • Shouldn't it be something like: `item["name"] = data.projectName; ` – Eliellel May 30 '18 at 18:02
  • 1
    `projectKey` and `projectName` are var scoped, which means they are scoped to the method, not the for each. Either change them to `let` or use a closure for your looping – Taplar May 30 '18 at 18:03

1 Answers1

1

The issue is because the outer loop has finished before all the AJAX requests complete and their callbacks run. As such, the projectKey, projectName and myURL variables all hold the values of the last iteration.

To fix this you can use a closure around the inner calls to maintain the scope of the variables you pass in:

var myOutput = [];
$.when(
  $.getJSON(projectListURL, function(data) {
    var compArr = data.components;
    for (i = 0; i < compArr.length; i++) {
      var projectKey = compArr[i].key;
      var projectName = compArr[i].name;
      var myURL = "http://myserver.com/projectdetails?projectkey=" + projectKey;

      (function(url, name) {
        $.getJSON(url, function(data) {

        }).then(function(data) {
          console.log("myURL:" + url);
          console.log("name:" + name);
          var item = {
            name: name,
            status: data.projectStatus.status
          };
          console.log(item);
          myOutput.push(item);
        });
      })(myURL, projectName);
    }
  })
).done(function() {
  console.log(myOutput);
});

Alternatively you can avoid the use of the closure and define the variables with the let keyword instead of var, but be aware that this will not work in < IE11.

Finally, note that in either case you can remove the callback handler function of the $.getJSON call as you're not using it, eg.:

$.getJSON(myURL).then(function(data) {
  // code...
});
Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
  • Thanks Rory. One question. What kind of variable will `myOutput` be? It's JSON.stringify(myOutput) comes blank - [ ]. And if I try myOutput[0] then its undefined. I want it to be a JSON. What am I doing wrong? – reiley May 30 '18 at 19:48
  • @reiley I think that code does not only suffer from scoping issues but also from $.when().done finishing too soon (I think it finishes when first request is done). Better use something like: `$.getJSON(projectListURL).then($.when.apply($,result.map(resultToPromiseLike)` full code can be found [here](https://jsfiddle.net/9a19ktyo/1/) – HMR May 30 '18 at 21:02