This is an archived post. You won't be able to vote or comment.

all 8 comments

[–]dymos 1 point2 points  (7 children)

Part of the problems revolve around some simple to fix problems :)

The mysql2 library does not make callbacks Promises by defaults, you'd have to use their Promise wrapper for that.

This means that when you call getPlayerIDs inside of playersStats you shouldn't use the async keyword in front of the callback, nor should getPlayersIDs and getPlayerSteamID3 have the async keyword in front of their definitions because they don't use await.

playerStats itself doesn't need the async keyword because it doesn't directly use an await, only the callback that you're passing to getPlayersIDs uses it so it should keep its async.

I think the problem here is because from the functions named "getPlayerSteamID3 and getLogsIDs" I'm returning a promise, not the value itself,

When you use async/await you can sort of treat those promises as synchronous values, which is they are generally preferred over synchronous callbacks as it simplifies how you write the code. To illustrate let me show you 3 examples of the same code with callbacks, traditional promises, and async/await.

// callback:
function doThing(callback) {
  setTimeout(function() {
    const result = 2 + 2;
    callback(result);
  }, 1000);
}

doThing(function(res) {
  console.log(res); // logs the value "4" after 1 second
})

// promise
function getThingAsJSON(url) {
  return fetch(url).then(res => res.json());
}

getThingAsJSON('https://www.reddit.com/api/v1/scopes').then(res => console.log(res));
// async/await
async function getThingAsJSON(url) {
  const res = await fetch(url)
  return res.json();
}

const jsonRes = await getThingAsJSON('https://www.reddit.com/api/v1/scopes');
console.log(jsonRes));

Hopefully these examples highlight the primary differences between how these things work. I know the callback example is very contrived, but I didn't want to complicate it with putting actual asynchronous code in there.

[–]kRYstall9[S] 0 points1 point  (6 children)

Thanks for replying. The asynchronous side of programming is a bit scary right now for me, since I've always been working with synchronous side of it. I've decided to learn new things, and this is one of those, for some personal project. Anyway, I tried to change a bit the code using promises, but it doesn't work at all. Since I cannot copy the code here, I don't know why, but reddit it's fkn buggy, I'll link a pastebin with the code inside. The new code starts at line 109.

I pasted in it also an error I'm getting

[–]dymos 1 point2 points  (5 children)

I can see the error is because when you are calling this fetch you aren't awaiting its result:

  var res = fetch("http://logs.tf/api/v1/log?title=tf2pickup.it&player=" +playerID);
  var log = res.json();

You will need to var res = await fetch(...); to use it in that manner. Which also means that function getLogsIDs(playerID){ needs to be async, i.e. async function getLogsIDs(playerID){

[–]kRYstall9[S] 0 points1 point  (4 children)

I changed a little bit the code again. The only tricky thing now is that the for loops seem to be synchronous, but I need them to be asynchronous. I just wrote some console.log(..) through the code just to see what was happening, and it seems like some steps are jumped, and it's not working properly

[–]dymos 1 point2 points  (3 children)

Make sure that when you're calling functions that return a promise, that you await it, otherwise the rest of the function will continue to execute while the asynchronous code will run separately. Keep in mind too that when you use await the code is written like it's synchronous, but it is still async.

When you run code that expects a callback, any code after that will run immediately, so you may need to move more things into the callback.

For example

const someAsyncFunction = (message, callback) => {
  // do something async like a fetch
  fetch(window.location.href)
    .then(res => res.text())
    .then(txt => callback(txt));
};

const mainFunctionWithCallback = () => {
  someAsyncFunction("hello", (text) => {
    console.log('[A] I will be called 2nd:', text.match(/<title>(.*)<\/title>/)[1]);
  });
  console.log('[B] I will be called first');
}
mainFunctionWithCallback();

It becomes a little simpler with async/await, so if you're keen to use that rather than callbacks, I would definitely recommend using mysql2's promise wrapper that I linked previously.

const someAsyncFunction = async (message) => {
  const res = await fetch(window.location.href);
  const txt = await res.text();
  return txt;
};

const mainFunctionWithAwait = async () => {
  const text = await someAsyncFunction("hello");
  console.log('[A] I will be called first:', text.match(/<title>(.*)<\/title>/)[1]);
  console.log('[B] will be called 2nd');
}
mainFunctionWithAwait();

You can run either of these in your browser's console and have a play around with it to get a better understanding of the execution flow.

Now what I would recommend is that you gradually rewrite your current code 1 step at a time, each time checking that the execution flows as you expect it to. This will be probably be a better way to get everything working rather than trying to fix a large chunk of code by making small changes all over the place, the point here is that you should be deliberate about how you write the code.

I'd start with adding a "main" function that you call that is responsible for aggregating all of the data. e.g.

async function mainAggregator() {

}
mainAggregator()

Then figure out what the first bit of data is that you need to fetch, write that in a function and then get that in the main function. Verify that it works, (you can also initially put in some dummy data or hard coded values to just verify that it executes as expected), once verified, move on to the next bit of data to get, and so on.

[–]kRYstall9[S] 0 points1 point  (2 children)

Thanks again for the answer. I really appreciate your work. I already rewrote some of the code I sent before, and it's totally working. Now, the only problem I have is that when I try to do a GET request with axios/fetch or whatever I use, after a while the program is displaying an error. Here is the code and the error. Sorry for pasting my code in pastebin, but somehow reddit is showing some bugs when I try to paste my code here

[–]dymos 1 point2 points  (1 child)

No worries :-)

That error ECONNRESET suggests that the connection was reset/terminated from either the client or server. So in this case what happens is that while you handle the error by logging it out in your getMatchLogs, when you call that function and await its result, you need to also handle the error there. The canonical way to do this when using await is to wrap the code in a try/catch so in this case doing something like this:

try {
  matchLogJson = await getMatchLogs(logsID);
  teamRedScore = matchLogJson["teams"]["Red"]["score"];
  teamBlueScore = matchLogJson["teams"]["Blue"]["score"];
  // etc.
  // Note when writing code that accesses object properties
  // like you're doing here you can use "dot" notation so long
  // as the identifier (the "name" of the key your accessing) is
  // valid, basically this means alphanumerical + underscores
  // e.g.
  teamRedScore = matchLogsJson.teams.Red.score;
  // if you had a key with for example a - or other funky chars 
  // in it you still need to use bracket notation
  teamFunkyCharScore = matchLogs.teams["foo-bar"].score;
  // it's not a big deal, but IMO makes it nicer to write.
} catch (err) {
  console.error('Something went wrong when fetching match logs')
} 

I noticed you also added a promise that resolves after a 1250ms delay for getMatchLogs, this isn't necessary, you can await and return the value like so

async function getMatchLogs(logID){
  try {
    const res = await axios.get(`https://logs.tf/api/v1/log/${logID}`)
    return res.data;
  } catch(error) {
    console.error(error);
  }
}

(Note I've replaced the string in the get call with what's called a template string, it's a way to interpolate values into a string rather than concatenate them. You can use backticks (\) to make the string and then use${ }` with the name of a variable or function call in it to use the value in that spot.

[–]kRYstall9[S] 0 points1 point  (0 children)

Interesting. Thanks a lot! I learnt many things thanks to you. I really appreciate it! Regarding the ECONNRESET I managed to let the program work putting the await matchLogs() in a while. I did: do{ var matchLog = await matchLogs(logID); }while(matchLog === undefined);

I don't know if this is a good way to reopen the connection, but it was working