0

I'm making (or mocking up) a website that allows League of Legends players to look up and compare stats of themselves and other players. However, I have an issue where the next page loads before the data is finished being retrieved.

I have tried placing all my functions in an async function, and calling that function later. I have also tried to set up Promises but I keep doing something wrong and getting myself confused.

For example, 2 of my main functions are

function getSummonerID(summonerRegion, summonerName) {
  return new Promise((resolve, reject) => {
    var dataSummoner = {};
    api.get(summonerRegion, 'summoner.getBySummonerName', summonerName)
      .then((data) => {
        if (data) {
          dataSummoner.id = data.id;
          dataSummoner.accountId = data.accountId;
          dataSummoner.name = data.name;
          dataSummoner.profileIconId = data.profileIconId;
          dataSummoner.summonerLevel = data.summonerLevel;
          dataSummoner.exists = true;
          dataSummoner.region = summonerRegion;
          console.log("\ngetSummonerID complete and exists\n");
        } else {
          dataSummoner.exists = false;
          console.log("\ngetSummonerID complete and doesnt exist\n");
        }
        resolve(dataSummoner);
      })
      .catch(error => console.log(error));
  })
}

and

function getHighestMastery(summonerRegion, summonerID) {
  var dataMastery = {};
  api.get(summonerRegion, 'championMastery.getAllChampionMasteries', summonerID)
    .then((data) => {
      dataMastery.championId = data[0].championId;
      dataMastery.championLevel = data[0].championLevel;
      dataMastery.championPoints = data[0].championPoints;
      for (var i = 0; i < Object.keys(championJSON.data).length; i++)
        if ((dataMastery.championId) === (championJSON.data[Object.keys(championJSON.data)[i]].id)) {
          dataMastery.championName = championJSON.data[Object.keys(championJSON.data)[i]].name;
          dataMastery.championTitle = championJSON.data[Object.keys(championJSON.data)[i]].title;
        }
    })
    .catch(error => console.log(error));
  console.log("\ngetHighestMastery complete\n");
  return dataMastery;
}

I'm then calling them

async function retreiveData(summonerRegion, summonerName) {
  summoner = await getSummonerID(summonerRegion, summonerName);
  summoner.name = summonerName;
  summoner.region = summonerRegion;
  console.log("summoner");
  console.log(summoner);
  if (summoner.exists) {
    mastery = await getHighestMastery(summonerRegion, summoner.id);
    console.log("mastery");
    console.log(mastery);
    matches = await getRecentGames(summonerRegion, summoner.accountId, 10);
    console.log("matches");
    console.log(matches);
    rankedInfo = await getRankedInfo(summonerRegion, summoner.id);
    console.log("rankedInfo");
    console.log(rankedInfo);
    console.log("calls done");
  }
  return "done";
}

and in turn, calling that here:

router.post('/summoner/submit', function(req, res, next) {
  summoner.region = req.body.summRegion;
  summoner.name = req.body.summName;
  if (summoner.name) {
    title = summoner.name + " on " + summoner.region + " - LOLSTATS.GG";
  }
  var x = retreiveData(summoner.region, summoner.name);
  console.log("x is:");
  console.log(x);
  x.then((testVar) => {
    console.log("test var: " + testVar);
    setTimeout(function() {
      console.log("summoner after 5s");
      console.log(summoner);
      console.log("mastery after 5s");
      console.log(mastery);
      console.log("matches after 5s");
      console.log(matches);
      console.log("rankedInfo after 5s");
      console.log(rankedInfo);
    }, 5000);
    res.redirect('/summoner/lookup');
  })
});

the console.logs of variables in retrieveData AFTER console.log(summoner) are all empty, but the data is there in the "after 5s" part, so I know my functions are working. I'm so confused and lost, sorry for the long post.

dubonzi
  • 1,492
  • 1
  • 21
  • 31
Neil R
  • 21
  • 9

1 Answers1

0

Try making getHighestMastery function async

async function getHighestMastery(summonerRegion, summonerID)

and awaiting the api.get() call instead.

var data = await api.get(summonerRegion, 'championMastery.getAllChampionMasteries', summonerID)

Also, try adding

.catch(error => {
    console.log(error));
    reject(error);
}

to the getSummonerID function

Josh
  • 451
  • 8
  • 22
  • Okay I added the await and the async, but in the console I get "UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: x.then is not a function", and then the redirect is never called – Neil R Apr 08 '18 at 18:27
  • I edited my answer. As someone else has commented as well, the `getHighestMastery` is where your race condition is happened. The function needs to return a Promise in order to be awaited. Keep in mind that async/await is just pretty syntax for Promises. You can only await a function if it returns a Promise (with a .then) and was defined as `async`. – Josh Apr 08 '18 at 18:34
  • Okay, I've got a few more functions similar to that, should I go through and do all the same to those too? – Neil R Apr 08 '18 at 18:36
  • `async/await` in my opinion is much cleaner to read and understand than a bunch of nested Promise chains. It's `async` code written in a linear fashion, you just have to understand that on top of the pretty syntax is just plain Promises under the hood. So generally if you're using an import or library that returns a Promise, you can just drop it right into your existing `async/await` code. For `getSummonerID` function, you could actually get rid of the Promise it returns, make the entire function `async` and just await your `api.get()` there as well. `async` automatically throws errors too. – Josh Apr 08 '18 at 19:05