0

I'm still quite a noob at Javascript and one thing that I'm struggling to wrap my head round is forcing synchronicity into a primarily asynchronous programming language.

I have a function that loads a decent size JSON file and another function that utilises the return of this function.

I have read a lot of other threads about similar issues and keep seeing very different solutions, I was just curious what the best way to handle this would be.

Constructor calling the two functions -

class Guild extends MapObject {
constructor(x,y,floor,type,name) {
    super(x,y,floor);
    this.levels = [];
    this.type = type;
    this.name = name;
    this.guildSelector();
    this.addLevels(this.name); 
    this.setTip();
}

guildSelector function that loads the JSON and returns it as this.guildData

guildSelector(){
    var oReq = new XMLHttpRequest();
    this.guildData = null;

    oReq.onload = reqListener;
    oReq.open("get", "/json/guilds.json", true);
    oReq.send();

    function reqListener() {
        this.guildData = JSON.parse(this.responseText);
        console.log(this.guildData);
        return this.guildData;
    }
}

addLevels function that currently runs before this.guildData is populated with the JSON causing it to crash as it can't find .guilds of null.

addLevels(name) {  

    if(name == "default"){
        this.name = "lowerSchoolOfEndurance";
    }

    console.log(this.guildData);

    this.guildData.guilds.forEach(function(guild) {
        console.log(guild.name);
        if(guild.name == name){
            for(var i = 1; i <= guild.levels.length; i++){
                var guildStats = guild.levels[i-1];
                for (var k in guildStats) {
                    console.log("Level "+i+" of the guild "+name+" increases "+k+" stat by "+guildStats[k]);
                    this.levels.push(guild.levels[i-1]);
                }
            }
        }
    });
    return this.levels;
}

So the TL:DR would essentially be, what is the best way to not trigger my class that requires the JSON to be loaded until the JSON is loaded.

Kind Regards, Earl Lemongrab

Farrell Coleman
  • 79
  • 3
  • 15
  • *forcing synchronicity into a primarily asynchronous programming language* — that is not going to work for you. You have to *embrace* the asynchronous nature of the language. – Pointy Aug 11 '17 at 14:37
  • I understand that in a general sense, but for this issue is that not exactly what I am doing? Surely there are cases where it becomes necessary to force synchronous behaviour? Anyways, the answer you linked to and the one given by Rob solve my problem very nicely, so thanks very much :) – Farrell Coleman Aug 11 '17 at 14:46

1 Answers1

2

You should use Promises:

guildSelector(){
    return new Promise((resolve, reject) => {
       var oReq = new XMLHttpRequest();
       this.guildData = null;

       oReq.onload = reqListener;
       oReq.open("get", "/json/guilds.json", true);
       oReq.send();

       function reqListener() {
          this.guildData = JSON.parse(this.responseText);
          resolve(this.guildData);
       }
   })
}

Then change your constructor to utilize the Promise:

this.guildSelector().then((guildData) => {
   this.guildData = guildData;
   this.addLevels(this.name);
});
Rob M.
  • 35,491
  • 6
  • 51
  • 50
  • Thank you! This worked perfectly. I was a little unsure of the syntax of Promise's so thought i'd avoid them, but it seems to be definitely worth more reading. Still have to wait 5 minutes before I can select this as the correct answer, but I will :p – Farrell Coleman Aug 11 '17 at 14:44
  • 1
    Glad to help, Earl. Promises are extremely helpful in many different scenarios and are absolutely worth spending some time learning. Cheers and good luck! :) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise – Rob M. Aug 11 '17 at 14:46