17

I need to get 8 JSON from 8 different URL. I stored the query string that I have to change in an Array and I loop through it with a for loop. Here is my code:

var index = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];

var request = new XMLHttpRequest();

for (var i = 0; i < index.length; i++) {

    var url = "https://wind-bow.glitch.me/twitch-api/channels/" + index[i];

    request.open("GET", url);
    request.onload = function() {
        var data = JSON.parse(request.responseText);
        console.log(data);
    }
    request.send();
}

So far I just want to display each JSON on the console. I don't get any error but I can display only the last JSON with the last index item (noobs2ninjas).

Could anybody explain me why? how do I get the all JSON that I need?

Thanks

Mihai Alexandru-Ionut
  • 47,092
  • 13
  • 101
  • 128
Dema
  • 199
  • 1
  • 2
  • 15

3 Answers3

20

Could anybody explain me why? how do I get the all JSON that I need?

In order to send a second request you need to wait for the first to finish. Hence, if you are interested to get the responses in the array order you can loop on each array element and only when you get the response you can loop on the remaining elements:

var index = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
var request = new XMLHttpRequest();
(function loop(i, length) {
    if (i>= length) {
        return;
    }
    var url = "https://wind-bow.glitch.me/twitch-api/channels/" + index[i];

    request.open("GET", url);
    request.onreadystatechange = function() {
        if(request.readyState === XMLHttpRequest.DONE && request.status === 200) {
            var data = JSON.parse(request.responseText);
            console.log('-->' + i + ' id: ' + data._id);
            loop(i + 1, length);
        }
    }
    request.send();
})(0, index.length);

Instead, if you want to execute all the request completely asynchronous (in a concurrent way), the request variable must be declared and scoped inside the loop. One request for each array element. You have some possibilities like:

  • using let
  • declaring a callback
  • using IIFE
  • using array .forEach() instead of a for loop

var index = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];


for (var i = 0; i < index.length; i++) {

    var url = "https://wind-bow.glitch.me/twitch-api/channels/" + index[i];

    let request = new XMLHttpRequest();
    request.open("GET", url);
    request.onreadystatechange = function() {
        if(request.readyState === XMLHttpRequest.DONE && request.status === 200) {
            var data = JSON.parse(request.responseText);
            console.log('-->' + data._id);
        }
    }
    request.send();
}

As per @Wavesailor comment, in order to make a math computation at the end of calls:

var index = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
var request = new XMLHttpRequest();
(function loop(i, length, resultArr) {
    if (i>= length) {
        console.log('Finished: ---->' + JSON.stringify(resultArr));
        return;
    }
    var url = "https://wind-bow.glitch.me/twitch-api/channels/" + index[i];

    request.open("GET", url);
    request.onreadystatechange = function() {
        if(request.readyState === XMLHttpRequest.DONE && request.status === 200) {
            var data = JSON.parse(request.responseText);
            console.log('-->' + i + ' id: ' + data._id);
            resultArr.push(data._id);
            loop(i + 1, length, resultArr);
        }
    }
    request.send();
})(0, index.length, []);
gaetanoM
  • 41,594
  • 6
  • 42
  • 61
  • What's the difference in using "onreadystatechange" compared to "onload"? – Dema Sep 30 '17 at 15:13
  • @Dema You need to wait the operation completes correctly while [load](https://developer.mozilla.org/en-US/docs/Web/Events/load): The load event is fired when a resource and its dependent resources have finished loading. – gaetanoM Sep 30 '17 at 15:18
  • I don't get yet why the loop function is able to keep the order in which the array elements are set (seems to me like is doing exactly what a for loop would do) but it does what you said so I guess I just need to think about it more thoroughly. Thanks for giving me the two options – Dema Sep 30 '17 at 17:18
  • @gaetanoM Great examples but could I ask you to explain the first one in more detail - I got lost with the manner in which you define the function `loop` and how it is called. Additionally how would I create a function that would use the results of the Ajax calls to calculate something which will be displayed i.e. can only calculate once I have all the values from the Ajax calls. – Wavesailor Jun 21 '19 at 17:10
  • @gaetanoM - IIFE - yes thanks that's it - I'll read up on it. With regards to the math operation: I'm guessing I should store the results in an array but I am finding it hard to figure out how to only let the math operation begin once all the Ajax calls are complete. i.e. ((result from Ajax call 1 + result from Ajax call 2) * result from Ajax call 3 * result from Ajax call 4. – Wavesailor Jun 21 '19 at 17:40
  • 1
    @Wavesailor Added a last snippet to the answer. I hope it helps. Thanks – gaetanoM Jun 21 '19 at 17:48
8

The problem is that you declare

var request = new XMLHttpRequest();

outside of the for loop. So you instance only one request.

You have to include it inside the for loop.

Also, don't forget that ajax is executed asynchronous so you will get the results in a random order.

The value of i variable must be declared using let keyword in order to declare a block scope local variable.

let allows you to declare variables that are limited in scope to the block.

let is always used as a solution for closures.

Also, you can use an array where you can store the XMLHttpRequest.

var index = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
requests=new Array(index.length);
for (let i = 0; i < index.length; i++) {
    var url = "https://wind-bow.glitch.me/twitch-api/channels/" + index[i];
    requests[i] = new XMLHttpRequest();
    requests[i].open("GET", url);
    requests[i].onload = function() {
        var data = JSON.parse(requests[i].responseText);
        console.log(data);
    }
    requests[i].send();
}
Mihai Alexandru-Ionut
  • 47,092
  • 13
  • 101
  • 128
6

You may also prefer to use the Fetch API in the place of XMLHttpRequest. Then all you have to do is to utilize a few Promise.all() functions.

var index = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"],
    url   = "https://wind-bow.glitch.me/twitch-api/channels/",
    proms = index.map(d => fetch(url+d));

Promise.all(proms)
       .then(ps => Promise.all(ps.map(p => p.json()))) // p.json() also returns a promise
       .then(js => js.forEach((j,i) => (console.log(`RESPONSE FOR: ${index[i]}:`), console.log(j))));
.as-console-wrapper {
max-height: 100% !important;
}
Redu
  • 25,060
  • 6
  • 56
  • 76