I'm pretty new to JavaScript and I've run into an issue I can't find a solution to. I'm developing a game for my APCS final project and I'm trying to add a leaderboard to it. To find the top players, I'm putting all the high scores in an array, sorting it from greatest to least, and then searching for 5 usernames whos scores match the first 5 numbers in the array. I'm using AppLab to create it and AppLab has a built-in database feature, and so that's what "readRecords is for. My issue though is that when I populate the array with a for loop, the populations don't exist outside of the function even though the array variable was created outside of the function, here's the code...
function leaderGrabEasy() {
var leaderScores = [];
var leaders = [];
readRecords("userData",{},function(records) {
for (var i = 0; i < records.length; i++) {
leaderScores.push(records[i].E_highscore);
}
leaderScores.sort(function(a, b){return b-a});
});
readRecords("userData",{E_highscore:leaderScores[0]},function(records) {
for (var i = 0; i < records.length; i++) {
leaders.push(records[i].username);
}
console.log(leaders);
});
}
The issue occurs when I try to read the database column "E_highscores" for whatever is in "leaderScores[0]"
readRecords("userData",{E_highscore:leaderScores[0]},function(records) {
But because the array is empty outside of the first function, that spot in the array is empty. Thanks in advance for any help!
-Indoors
readRecords
is an asynchronous function, correct?The problem is that you have a race condition. That means code needs to be executed in a very specific order in order to work, but you are not currently in control of that order.
Scores get pushed to
leaderScore
inside the callback that gets passed toreadRecords
. However, on the very next line (after callingreadRecords
), you are trying to callreadRecords
again, with a value that is populated inside the callback to the firstreadRecords
.Basically, your code is executing in this order:
In fact, there's no guarantee
callback1
will happen beforecallback2
!There's any number of tools to address this asynchronous ordering issue, like Promises and such. I'm going to go with the simplest option, and just rearrange your code. This will create nested callbacks, but for right now, it works and will (hopefully) help you to see this principle in action.
Now the second
readRecords
will only happen after you have populated the array, not any time before.This does open up other issues with re-usability. What if you want to later do more complex things, or add more callbacks, perhaps even a loop? Well that's going to be on you haha. But you can definitely do it successfully if you keep in mind the principle we discussed here: any asynchronous callback can be called at any time really, so always right your code with that in mind.
Ask yourself the question: will my code work if this callback is called inline (synchronously, in the order it is written), in 1ms, 500ms, and 1 minute? If so, then it is logically sound.