0

I've got a website that has a user page with the following relationships:

  • User page needs a list of 'Post' objects.
  • Post objects need a list of 'Media' strings (By 'string', I mean I only need the title string field from the specific column).

Post objects and media strings objects are both obtained from my database, meaning I need to first query for posts matching the users ID, then query for media objects that match each post object.

My main question is: How would I reformat my current code (below) to support the above described functionality?

I haven't tried a lot because I'm not even sure where to start. My first attempt was isolating my queries to their own asynchronous functions which I could then await, but that just threw errors like "await is only valid in async function". I also looked into promises, but it all went over my head.

My current code shows my function method where I first call to get all relating posts with (users[0] being the user object queried by user specified input):

// I pass this to handlebars for partial renders
let posts = getPostsForUser(users[0].user_id);

and my query functions are defined as such:

function getPostsForUser(uid) {
    pool.query('SELECT post_id, body, date_time FROM Post WHERE Post.author = ?', [uid], (err, result) => {         
        if (result.length) {
            let posts = [];

            // Put all posts into the posts array
            for (i = 0; i < result.length; i++) {
                let subs = getMediaForPost(result[i].post_id);
                posts.push({
                    "postContent": result[i].body,
                    "timestamp": result[i].date_time,
                    "subjects": subs
                });
            }

            return posts;
        } else return [];
    });
}

function getMediaForPost(pid) {
    // Returns an array of media objects queried to match the given pid
    pool.query('SELECT title FROM Media WHERE media_id IN (SELECT media_id FROM Post_Media WHERE post_id = ?)', [pid], (err, result) => {
        if (result.length) {
            let postMedia = [];

            for (i = 0; i < result.length; i++)
                postMedia.push({"postSubject": result[i].title});

            return postMedia;
        } else return [];
    });
}

All of my queries give me the correct information, I just don't know how to wait for said queries to finish before I render my user page.

In the end, "posts" should contain an array of 'Post' objects, and each 'Post' object should contain an array of 'Media Objects' matching the user in my db.

Hichem BOUSSETTA
  • 1,791
  • 1
  • 21
  • 27
lavaskin
  • 21
  • 7

1 Answers1

0

Try This

let posts = await getPostsForUser(users[0].user_id);

function getPostsForUser(uid) {
    return new Promise((resolve, reject) => {
        return pool.query('SELECT post_id, body, date_time FROM Post WHERE Post.author = ?', [uid], async (err, result) => {
            if (err) return reject(err);
            if (result.length) {
                let posts = [];

                // Put all posts into the posts array
                for (i = 0; i < result.length; i++) {
                    let subs = await getMediaForPost(result[i].post_id);
                    posts.push({
                        "postContent": result[i].body,
                        "timestamp": result[i].date_time,
                        "subjects": subs
                    });
                }
                return resolve(posts);
            } else return resolve([]);
        });
    })

}

function getMediaForPost(pid) {
    // Returns an array of media objects queried to match the given pid
    return new Promise((resolve, reject) => {
        return pool.query('SELECT title FROM Media WHERE media_id IN (SELECT media_id FROM Post_Media WHERE post_id = ?)', [pid], (err, result) => {
            if (err) return reject(err);
            if (result.length) {
                let postMedia = [];

                for (i = 0; i < result.length; i++)
                    postMedia.push({ "postSubject": result[i].title });

                return resolve(postMedia);
            } else return resolve([]);
        });
    });
}

in this way you can use data that was passed by callback since you can't return data to function within a callback, you can use a promise, and to use a promise without a callback to use variable elsewhere in the scope you can use await/async.

Note: this will block the loop and perform the task.

Read More About await/async Here And Here

Edit: The Error await is only valid in async function happens because you have to add async before Function name.

Jamal Abo
  • 472
  • 4
  • 16
  • Similar to what I described in my question, I'm getting the "SyntaxError: await is only valid in async function" error message. I tried adding "async" to the front of the two query functions, but that didn't change anything. The offending line seems to be the "posts" variable definition. – lavaskin May 18 '19 at 02:15
  • where is your `posts` placed (where is the line `let posts = await getPostsForUser(users[0].user_id);`) – Jamal Abo May 18 '19 at 02:18
  • It's inside a query inside a get request handler. Here's the full snippet of its location: https://pastebin.com/5NLtWSsC. Also, I know I'm using "templatePosts" instead of "posts." I want to get posts working before I pass it to the client. – lavaskin May 18 '19 at 02:20
  • replace this `pool.query('SELECT username, user_id FROM User where USERNAME = ?', [name], (err, users) => {` with this `pool.query('SELECT username, user_id FROM User where USERNAME = ?', [name], async (err, users) => {` or add async to the first query callback. – Jamal Abo May 18 '19 at 02:21
  • That finally got it to run without errors (though it throws some strange ones when I actually send a get request). Thank you for all your help! I'll make sure to look into async methods as it seems I'll be using them a lot nowadays. – lavaskin May 18 '19 at 02:23