user2869150
user2869150

Reputation: 343

Running javascript loop, asynchronous results

I got 50 JSON files. (1.json, 2.json ... 50.json). The structure for each one is:

{
  "book": "Księga Wyjścia",
  "chapter": 1,
  "type": "verses",
  "verses": [
    {
      "text": "Oto imiona synów Izraela, którzy razem z Jakubem przybyli do Egiptu. Każdy zaś przyszedł ze swoją rodziną:",
      "verse": "1"
    },
    {
      "text": "Ruben, Symeon, Lewi, Juda;",
      "verse": "2"
    },
    {
      "text": "Issachar, Zabulon i Beniamin;",
      "verse": "3"
    },
    {
      "text": "Dan, Neftali, Gad i Aser.",
      "verse": "4"
    },
    {
      "text": "Było zaś wszystkich potomków Jakuba siedemdziesiąt osób, Józef zaś już był w Egipcie.",
      "verse": "5"
    }
  ]
}

There are more verses in every file and the size of each one can be completely different (so they don't load immediately). I load "chapter" node. Each time I refresh the file, I got different order, ie. (just last few numbers from last reshresh): 28,35,32,36,37,29,30,31,38,33,49,50,39,40,41,42,43,44,45,46,47,48

<script>
  for (i = 1; i <= 50; i++) {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
        var input = JSON.parse(this.responseText);
        console.log(input.chapter);

      }
    };
    xmlhttp.open("GET", (i + ".json"), true);
    xmlhttp.send();
  }
</script>

I assume it's because it loads asynchronous. Can I force my script to load synchronous?

Upvotes: 0

Views: 50

Answers (2)

Talha Akbar
Talha Akbar

Reputation: 10030

You should keep the requests asynchronous because not only it would save the loading time but, if any of the requests take too long to respond, no other chapter will be loaded until that request resolves.

As a start, I would suggest you create a responses array and in the callback, push the JSON response to that array and check for the number of responses received so far, when the number responses are 50, it means all of the chapters are loaded. At that point, sort() the responses based on chapter. Once done, you will then be able to go ahead and doSomething().

Note: You need to define doSomething() function that takes all of the chapters in order as an input and does something with them.

<script>
var responses = [];

for (i = 1; i <= 50; i++) {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            var input = JSON.parse(this.responseText);
            responses.push(input);

            if (responses.length === 50) {
                responses = responses.sort(function(a, b) {
                    return a.chapter < b.chapter;
                });
                doSomething(responses);
            }
        }
    };
    xmlhttp.open("GET", (i + ".json"), true);
    xmlhttp.send();
}
</script>

Like @MoshFeu and @trincot suggested, it is way easier to use fetch and if you get your head around Promise, you will be far better off with Promise and async/await syntax for all the asynchronous work.

Upvotes: 0

trincot
trincot

Reputation: 350137

Use the fetch API and the power of promises:

let promises = [];
for (let i = 1; i <= 50; i++) {
    promises.push(fetch(i + ".json").then(resp => resp.json());
}
Promise.all(promises).then(function(inputs) {
    inputs.forEach(input => console.log(input.chapter));
});

Even though the promises may resolve in a different order, Promise.all will resolve to an array of responses that maintains the original order of the promises it got.

NB: make sure you define your variables (like i) with var, let or const

Upvotes: 3

Related Questions