What's the best way to handle nested asynchronous functions that need data from eachother? [duplicate]

33
May 19, 2019, at 11:50 AM

This question already has an answer here:

  • How do I return the response from an asynchronous call? 35 answers

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.

Answer 1

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.

READ ALSO
Search for a string name inside object with spaces

Search for a string name inside object with spaces

I have an object like this which contain a string name with whitespaces :

11
Empty return of variable

Empty return of variable

I have this code:

44
How do I host my React App with Namecheap?

How do I host my React App with Namecheap?

So previously I've deployed React Apps on Heroku and Github and never had a problem but now when I try and get a dang app up on Namecheap's hosting I'm having a million problems

31