Shashank Shetti
Shashank Shetti

Reputation: 33

How to use await inside await in async method and return result in last?

am using await inside await, first i have used await in line 'a', it works fine and enters inside continues to execute from line 'b' to line 'd', but after that it needs to go inside that query and has to execute from line 'e' to line 'f', then it has to execute line 'g' and 'h' finally and return result but problem is after line 'd' it will not execute inside code instead it comes to line 'g' and executes further code and then comes back to line 'e' which causes error. Please help me out.


    var writeStreamData = '';
    var surveyBlockId = req.body.surveyBlockId;
    var title = req.body.title;
a.    await SurveyAnswer.find({'blockId': surveyBlockId}, (err, doc) => {
b.        if(doc.length!=0){
            userName = '';
            userIdList = Object.values(doc.reduce((acc,cur)=>Object.assign(acc,{[cur.userId.toString()]:cur}),{}));
            const pdfDoc = new PDFDocument({margin: 50});

            writeStreamData = pdfDoc.pipe(fs.createWriteStream('pdf_files/SurveyAnsweredUserList.pdf'));

            pdfDoc.fontSize(25).text('Home Owners Association', {align: "center"});
            pdfDoc.lineCap('butt').moveTo(30,80).lineTo(600,80).stroke();
            pdfDoc.moveDown();
            pdfDoc.fontSize(20).text('Survey Title : '+title);
            pdfDoc.moveDown();
            pdfDoc.fontSize(20).text('List of Users');
            if(doc.userIdList!=0){
                var counter = 0;
c.                userIdList.forEach(async element => {
                    userId = element.userId.toString();
d.                    await User.findOne({'_id':userId},async(err1, doc1) => {
e.                        if(!err1){
                            counter++;
                            userName = doc1.firstname + " "+ doc1.lastname;
                            pdfDoc.fontSize(15).text((counter)+". "+userName).moveDown();
                        }else{
                            counter++;
                            //return eachCb(err1)
                        }
                        //eachCb();
                    })
                }, function(err, data) {
                    if(err){
                        response = {
                            "message": "failed"
                        }
                        res.send(response);
                    }
                });
            }else{
                pdfDoc.fontSize(15).text('No User answered');
            }
f.            pdfDoc.end();
        }else{
            response = {
                "message": "failed"
            }
            res.send(response);
        }
    });

g.    fs.readFile(writeStreamData.path, function(err, data) {
h.        if(err) throw err;
        const pdf = data.toString('base64');
        response = {
            "message": "success",
            "data": pdf
        }
        res.send(response);
    })   
});```

Upvotes: 0

Views: 1736

Answers (3)

lousybear
lousybear

Reputation: 451

You can make use of Promise inside Promise instead of async await to get your work done. I have an example similar to yours which i am using to fetch data from s3 bucket. You can take reference to implement promise inside promise.

 new Promise((lresolve) => {
        s3.listObjectsV2(params, (err, devicedata) => {
            if (!err) {
                const data = devicedata.CommonPrefixes;
                data.forEach(value => {
                    const prefix = value.Prefix.replace(params.Prefix, '').replace('/', '');
                    devices.push(prefix);
                });
                lresolve(devices);
            }
        });
    }).then(qDevices => {
        const devicePromise = [];
        qDevices.forEach(devicepath => {
            devicePromise.push(
                new Promise((qresolve) => {
                    params.Prefix = `${staticPrefix + devicepath}/tyre/`;
                    let processedData = [];
                    s3.listObjectsV2(params, (err, data) => {
                        if (!err) {
                            const contents = data.Contents;
                            const fetch = [];
                            contents.forEach(content => {
                                fetch.push(getObject(content))
                            });
                            Promise.all(fetch).then(data => {
                                data.forEach(d => {
                                    processedData = processedData.concat(d);
                                });
                                qresolve(processedData);
                            });
                        }
                    });
                }));
        });

Upvotes: 0

Peter Zeller
Peter Zeller

Reputation: 2296

In my opinion the cleanest way is to write wrappers for functions like fs.readFile that return a promise instead of using a callback. This makes it easier to compose everything with await.

You can use bluebird to automate this conversion as shown in this answer: https://stackoverflow.com/a/34642827/303637

Upvotes: 0

Shihab
Shihab

Reputation: 2679

You are doing a few things wrong. You can only await a promise. SurveyAnswer.find() receives a callback function, it doesn't return a promise. So, you can't await it. Same goes for d and g. As you are using callback style so await in front of a, d and g is useless.

Also, await does not work in forEach function. Use for...of instead.

Upvotes: 1

Related Questions