1

I'm beginner to a JavaScript. I developed one code to fetch some URLs output using fetch API. When I used below code it says, Functions declared within the loop is referencing an outer scoped variable. I understand that it is due to outer scope of obj variable but how can I make it work. Kindly help. Below is the code,

var obj = [
  {"Id":"10101","descr":"server1.com"},
  {"Id":"10102","descr":"server2.com"},
  {"Id":"10103","descr":"server3.com"},
  {"Id":"10104","descr":"server4.com"},
  {"Id":"10105","descr":"server5.com"},
  {"Id":"10106","descr":"server6.com"}, 
  {"Id":"10107","descr":"server7.com"}
];

var temp = [];
for (var i = 0; i < obj.length; i++){      
  
  var id = obj[i].Id;      
  fetch('https://abced.com/api/'+id+'/value', {
    method : "GET",
    headers: { "Authorization": "xyz" }
  })
    .then(res => res.json())
    .then(data => { 
      var stats = data.status;          
      if (stats != "OK") {
        temp.push({ Id: obj[i].Id, descr: obj[i].descr, value:"ERROR" });
      }
      console.log(temp);
    })
    .catch(x => console.log("fail:", x))

}

My expected output is. (values of Id and descr will depends on if statement in the code)

[{"Id": "10101","descr": "server1.com","status": "ERROR"},
{"Id": "10103","descr": "server3.com","status": "ERROR"},
{"Id": "10104","descr": "server4.com","status": "ERROR"}]
Gershom Maes
  • 7,358
  • 2
  • 35
  • 55
  • 4
    Your code has quite a few basic syntax errors, unfortunately. Can you fix those please and edit your question accordingly? – Patrick Hund Jan 19 '23 at 15:49
  • 2
    [The closure explained](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example). – Teemu Jan 19 '23 at 15:50
  • 1
    And please indent your code to improve the readability. – Mark Baijens Jan 19 '23 at 15:52
  • 2
    You should work with async: https://dmitripavlutin.com/javascript-fetch-async-await/ – Jonas Jan 19 '23 at 15:55
  • 1
    You may also want to read up on asynchronous events in javascript in general: https://www.digitalocean.com/community/tutorials/understanding-the-event-loop-callbacks-promises-and-async-await-in-javascript – Gershom Maes Jan 19 '23 at 16:02
  • push the fetch promise which resolves the value to an array of promises, then use Promise.all to do something once they have all resolved https://playcode.io/1078971 don't go down the async/await route else the for loop will be blocking – Lawrence Cherone Jan 19 '23 at 16:15
  • The message is probably not referring to `obj` and `tmp`, but more likely to `i` and `id`. Both `i` and `id` are defined within the for-loop using `var`. This means there is one single `i` and `id` variable that is shared between the different iterations. By the time the `.then()` callbacks are called, `i` is no longer the same but instead is `obj.length`. `id` will be set to the last `id` in the loop. As a rule of thumb, always use `let` or `const`. Only use `var` if you have a specific reason for doing so. Defining `i` and `id` with `let` means that each iteration has its own variable. – 3limin4t0r Jan 19 '23 at 16:47

1 Answers1

2

I'd suggest using the async...await syntax, this is a lot easier to read than lots of .then() handlers.

We'd use a for .. of loop to iterate through each Id, descr value in our obj array, making the relevant fetch() call for each one.

We await the result of each fetch call, then test the status. If it's not OK we add to the results array.

var obj = [
  {"Id":"10101","descr":"server1.com"},
  {"Id":"10102","descr":"server2.com"},
  {"Id":"10103","descr":"server3.com"},
  {"Id":"10104","descr":"server4.com"},
  {"Id":"10105","descr":"server5.com"},
  {"Id":"10106","descr":"server6.com"}, 
  {"Id":"10107","descr":"server7.com"}
];

async function getResults() {
    const results = [];
    for(let { Id, descr} of obj) {
        const data = await fetch('https://abced.com/api/' + Id + '/value', {
          method : "GET",
          headers: { "Authorization": "xyz" }
        }).then(res => res.json());
        if (data.status !== 'OK') {
            results.push({ Id, descr, value: 'ERROR' })
        }
    }
    return results;
}

async function test() { 
    const results = await getResults();
    console.log('Results:', results)
}

test();

Below is a snippet where I've mocked out fetch, this should give you an idea of what to expect.

fetchMock returns a status of 'BAD' for ids 10101, 10103, 10104 and 'OK' for all the others.

// For testing only, replace with fetch when appropriate...
function fetchMock(url) {
    let id = url.split('/')[4];
    if ([10101, 10103, 10104].includes(+id)) {
        return Promise.resolve({ json() { return Promise.resolve({ status: 'BAD'})}})
    } else {
        return Promise.resolve({ json() { return Promise.resolve({ status: 'OK'})}})
    }
}

var obj = [
  {"Id":"10101","descr":"server1.com"},
  {"Id":"10102","descr":"server2.com"},
  {"Id":"10103","descr":"server3.com"},
  {"Id":"10104","descr":"server4.com"},
  {"Id":"10105","descr":"server5.com"},
  {"Id":"10106","descr":"server6.com"}, 
  {"Id":"10107","descr":"server7.com"}
];

async function getResults() {
    const results = [];
    for(let { Id, descr} of obj) {
        const data = await fetchMock('https://abced.com/api/' + Id + '/value', {
          method : "GET",
          headers: { "Authorization": "xyz" }
        }).then(res => res.json());
        if (data.status !== 'OK') {
            results.push({ Id, descr, value: 'ERROR' })
        }
    }
    return results;
}

async function test() { 
    const results = await getResults();
    console.log('Results:', results)
}

test()
.as-console-wrapper { max-height: 100% !important; }
Terry Lennox
  • 29,471
  • 5
  • 28
  • 40
  • This seems to be working but it is also returning a promise pending along with array **Promise {}** **(3) [{…}, {…}, {…}]** I have another piece of code which sitting the above code that returns value only, here is that code snippet `fetch('https://getserver.com/api').then((res) => res.json()).then(function(data) { var value = data.values; const formatData = (temp) => { return temp.map(({ Id, value: {descr}}) => ({Id, descr})); }; var output = formatData(value); console.log(output) }).catch(e => { console.log("fetch fail: " + e);});` – user3046893 Jan 19 '23 at 18:31
  • It'll return a promise pending if you don't await it in the calling function. You can also call .then() on the result. – Terry Lennox Jan 19 '23 at 18:31
  • 1
    Is it possible write whole code with ".then" notation because that seems to be working for my system. (Like the snippet I added in previous comment) – user3046893 Jan 20 '23 at 18:11
  • Thanks for suggestions. This issue is sorted. – user3046893 Jan 22 '23 at 16:10