-2

I am fairly new to web development as a whole, so I don't understand promises and async/await much. I'm trying to build a job posting site as a project, but got stuck as I'm unable to make a particular piece of code behave as I want.

In the following snippet, the recruiter who posted a particular job requirement wants to see the skills of the applicant that are relevant to the job requirement. So the code would first get the job details and the details of the applicant in question from the mongodb collection. Then it'll try to first store the required skill of the applicant and then all the skills of the applicant in the skills array. Only after storing all of the above data in skills[], should the code should respond with the last json. But it's not behaving like I want and its responding before the storing is complete. Can someone guide me on how to make it "synchronous"?

NOTE: I removed all the error handling to make the snippet shorter. Also, the applicant has 2 required skills out of overall 5 skills.

router.post('/recruiter/viewApplicant', passport.authenticate('jwt', {session: false}), (req, res, next) => {

    if(req.user.accountType === "Recruiter") {
        skills = [];

        Job.getJobByID((req.body.jobID),(err, job) => {
            User.getUserByEmail(req.body.email,(err, user) => {
                let i =0;
                job.requiredSkills.requirements.forEach(reqSkill => {
                    user.classData.forEach(classDetail => {
                        if(classDetail.classID === reqSkill.class.replace(/ /g, '-')) {
                            ClassData.getClassByclassID(classDetail.classID,(err, classInfo) => {
                                skills.push({
                                    type: 'req',
                                    imgURL: classInfo.imgURL,
                                    skill: classDetail
                                })
                                console.log('req: '+i)
                                i++
                            })
                        }
                    })
                })

                let k=0
                user.classData.forEach(classDetail => {
                    ClassData.getClassByclassID(classDetail.classID,(err, classInfo) => {
                        skills.push({
                            type: 'all',
                            imgURL: classInfo.imgURL,
                            skill: classDetail
                        })
                        console.log('all: '+k)
                        k++
                    })
                })
            }) 

            console.log("Completed");        
            res.json({success: true, msg: "Successful", data: skills});
        })
    }
});

Expected Result:

req: 0  
req: 1   
all: 0  
all: 1  
all: 2  
all: 3  
all: 4  
Completed  

Actual Result:

Completed  
req: 0  
req: 1  
all: 0  
all: 1  
all: 2  
all: 3  
all: 4  
  • 1
    Please create [*minimal* complete verifiable example](https://stackoverflow.com/help/mcve). Removing the error handling is a start, but you've got 100 different things going on in that massive code dump. Reduce it to one, preferably no more than 20~30 LOC. You can demostrate use of async/await in about ~10. – Jared Smith Feb 09 '19 at 13:40
  • 1
    Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Jared Smith Feb 09 '19 at 13:42
  • I saw this very same post earlier, but was unable to use async/await properly. – KevinTenyRoy Feb 09 '19 at 13:52
  • For future reference to prevent your questions from being closed prematurely, if you have a question *about* a question here on SO and/or it's answers you need to include that information in *your* question, including a link to the specific question and possibly a link to the specific answer you don't understand. Please edit your question to be *specifically* about what you are failing to understand from the answers to the other question (e.g. [this one](https://stackoverflow.com/a/14220323/3757232)). Otherwise it's just a dupe and will be closed as such. – Jared Smith Feb 09 '19 at 14:00
  • I've tried many solutions given on SO and other forums/articles for the past two days. They seem to work fine when I try it on a smaller code, but it's not working when I try it on the code above. That's why I posted the code snippet here hoping that someone could make it "synchronous". This way I can learn exactly how to use async/await or promises. – KevinTenyRoy Feb 09 '19 at 14:08
  • 1
    It's already asynchronous and cannot be made synchronous, that's what dupe question is about. It may look similarly to synchronous with async/await, but only because it was designed to look like that. I'd suggest to start with raw promises and move to async/await once you will understand them well, since async/await is syntactic sugar for promises, you can't use it efficiently without knowing what desugared code looks like. *hoping that someone could make it "synchronous"* - SO is helpful community but "write the code for me" questions that lack research aren't received well. – Estus Flask Feb 09 '19 at 14:24

1 Answers1

0

To get the code you have to work, move the res.json() call as follows

            user.classData.forEach(classDetail => {
                ClassData.getClassByclassID(classDetail.classID,(err, classInfo) => {
                    skills.push({
                        type: 'all',
                        imgURL: classInfo.imgURL,
                        skill: classDetail
                    })
                    console.log('all: '+k);
                    k++;
                })
            })
            res.json({success: true, msg: "Successful", data: skills});

It doesn't work where you put it because your call to .getJobById() returns immediately. When it finishes the work you want from it, you know because it invokes its callback function. The same is true of the nested .getUserByEmail() call.

async/await and Promise are ways to organize Javascript callbacks so they do not appear to be nested. They make code easier to read. But the callbacks still are nested and asynchronous. Explaining exactly how to convert your code to use Promises and async/await is beyond the scope of a SO answer. In short, you should promisify your asynchronous functions, then you can await their results. It is absolutely worth a day or two of your time to get familiar with this way of coding in Javascript.

Pro tip: If you step through this kind of code with your debugger, use Step Into a lot.

O. Jones
  • 103,626
  • 17
  • 118
  • 172