2

I am currently working on a webproject for a Twitch-Community in node.js. I'm using the Twitch API to receive the profilepictures and adding them with other information into an array of objects, before the page will be rendered. Unfortunately I'll only get a pending promise for the profile pictures URL, when the page is rendered. nearly a millisecond after the page will be rendered, I get the console log about the PB-Links, so it goes fast, so an ajax dosen't make sense in my opinion. Is there any possibility to wait until the promises are loaded before the script goes on to render the page?

Thank you in advance

async function getStreamersPB(streamerName){
  var streamer = await twitch.getUsers(streamerName);
  var streamerPBURL = await streamer.data[0].profile_image_url;


  console.log(streamerPBURL);
}

...

router.get('/', (req, res) => {

  var streamer = [];
    con.query("SELECT * FROM xxx",  function (err, result) {
    if (err) {
      console.log(err);
    }
    if (result.length !== 0){
      for (let i = 0; i < result.length; i++) {
        let streamObj = {
          "id": result[i].id,
          "name": result[i].name,
          "website": result[i].website.toLowerCase(),
          "status": result[i].status,
          "rang": result[i].rang,
          "userPB": getStreamersPB(result[i].name)
        }
        console.log(streamObj);
        streamer.push(streamObj);
    }}});

    con.query("SELECT * FROM xxx WHERE xxx = '" + xxx + "'", function (err, result) {
    if (err) {
      console.log(err);
    } if(result.length === 1){
    res.render('home', {
      message: result[0].twitchName,
      userID: result[0].id,
      streamer: streamer
    });

...

That is my Console log:

{
  id: 1,
  name: 'xxx',
  website: 'xxx',
  status: 0,
  rang: 1,
  userPB: Promise { <pending> }
}
https://static-cdn.jtvnw.net/...xxx

Maggimann
  • 63
  • 1
  • 7
  • 1
    You need to await `getStreamersPB`. Mark `function (err, result)` as `async` so you can await it – slebetman Aug 25 '21 at 16:05
  • Hey @slebetman I changed the code to `(async function (err, result) {await getStreamersPB(result[i].name)})` now I am getting console log `userPB: [AsyncFunction: userPB]` – Maggimann Aug 25 '21 at 16:11

4 Answers4

0

As slebetman mentioned, you have to await the async function call, or else you'll send a pending promise to your client. Here's your code with the modification.

router.get('/', (req, res) => {

  var streamer = [];

  // This function has been marked as async so we can use await in it.
  con.query("SELECT * FROM xxx", async function (err, result) {

    if (err) {
      console.log(err);
    }

    if (result.length !== 0) {
      for (let i = 0; i < result.length; i++) {

        let streamObj = {
          "id": result[i].id,
          "name": result[i].name,
          "website": result[i].website.toLowerCase(),
          "status": result[i].status,
          "rang": result[i].rang
        }

        // getStreamersPB is an asynchronous method which you must await.
        streamObj.userPB = await getStreamersPB(result[i].name);

        console.log(streamObj);
        streamer.push(streamObj);
      }
    }

    // I moved this query to inside the callback for the first query
    // to ensure this query happens after the first one. Originally,
    // both `con.query` calls were being invoked at the same time, so
    // there's no garuntee in what order they execute.
    con.query("SELECT * FROM xxx WHERE xxx = '" + xxx + "'", function (err, result) {
      if (err) {
        console.log(err);
      } if (result.length === 1) {
        res.render('home', {
          message: result[0].twitchName,
          userID: result[0].id,
          streamer: streamer
        });
      }
    });

  });

});

Dylan Landry
  • 1,150
  • 11
  • 27
  • Thank you really much, but now I'm getting a SyntaxError: `SyntaxError: await is only valid in async functions and the top level bodies of modules` I also tried to set the other function on async, but then I get the result: `userPB: undefined` – Maggimann Aug 25 '21 at 16:22
  • @Maggimann I updated the answer. I had the wrong function marked as async. Also, your first code assumed the two `con.query` calls happened one after the other. That isn't garunteed, their callbacks can happen in any order. So I put the second `con.query` inside the callback of the first `con.query`. When dealing with asynchronous function calls (either callbacks or promises) you have to be careful about which order functions will finish. You don't want to respond to the http request before the data you are fetching has returned. – Dylan Landry Aug 25 '21 at 16:39
  • Now I'm getting a console log of `userPB: undefined` – Maggimann Aug 25 '21 at 16:52
0

I got it fixed! I forgot to put a return statement into my function!

Thank you very much everyone for your answers and your help!!!

Maggimann
  • 63
  • 1
  • 7
-1

You could use css to hide the page at first, then when your profile pictures have loaded, use javascript to change the css and reveal the page.

Dylan Landry
  • 1,150
  • 11
  • 27
  • That would work if the JavaScript was client side … which it isn't. – Quentin Aug 25 '21 at 16:03
  • Thank you for your answer. This is not possible in my way, because I don't want to use ajax or anything like that, because I am rendering the page with the objects. So the site will appear in different way, as more objects the array has. – Maggimann Aug 25 '21 at 16:06
  • Oh sorry, I missunderstood. I thought this was an issue of images loading after initial page rendering. I didn't realize it was an async/await issue. The comment from slebetman explains your issue. – Dylan Landry Aug 25 '21 at 16:12
  • Still I'm very thankful for your answer! Check my answer on slebetman's command. That isn't working either :( – Maggimann Aug 25 '21 at 16:15
  • @Maggimann I posted an answer with code that shows slebetman's comment. – Dylan Landry Aug 25 '21 at 16:17
-1

Since you are receiving an error to Dylan's answer, add async to the first com.query function.

router.get('/', async (req, res) => {

  var streamer = [];
    con.query("SELECT * FROM xxx", async function (err, result) {
    if (err) {
      console.log(err);
    }
    if (result.length !== 0){
      for (let i = 0; i < result.length; i++) {
        
        let streamObj = {
          "id": result[i].id,
          "name": result[i].name,
          "website": result[i].website.toLowerCase(),
          "status": result[i].status,
          "rang": result[i].rang
        }

        // getStreamersPB is an asynchronous method which you must await.
        streamObj.userPB = await getStreamersPB(result[i].name);

        console.log(streamObj);
        streamer.push(streamObj);
    }}});

    con.query("SELECT * FROM xxx WHERE xxx = '" + xxx + "'", function (err, result) {
    if (err) {
      console.log(err);
    } if(result.length === 1){
    res.render('home', {
      message: result[0].twitchName,
      userID: result[0].id,
      streamer: streamer
    });

}
  • Hi @infinite_verma thank you very much for your answer! Unfortunately I'm getting a console log of `userPB: undefined` here :( So this isn't working either – Maggimann Aug 25 '21 at 17:01
  • 1
    @Maggimann Did you **return** anything in `getStreamersPB()`? The code you posted does not return anything but only print the output but I assumed that was simply incomplete example. If your code REALLY did not return anything then that's why the value is `undefined`. In javascript the default return value is `undefined` so if a function returns nothing it will return undefined (or in this case a Promise of undefined) – slebetman Aug 25 '21 at 17:28