1

I'm getting started with NodeJS and i'm blocked with this problem for more then two days now! if someone can help i will be very thankfull!

i'm trying to get a query result, then map all results and apply another uery then render both query results. here is my code :

app.get('/home/', function(req, res) { 
let sessionUser = req.session.user;
if(sessionUser) {
    user.findMemosByUserId(sessionUser.id, function(result) {
        result.forEach(function(memo){
            //get users having acces to each memo
            user.findUsersByMemoId(memo.id, function(result2){
                var obj = {};
                obj[memo.id] = [];
                result2.forEach(function(res){
                    obj[memo.id].push(res.username);
                });
                req.session.sharings.push(obj);
                console.log(req.session.sharings); //it shows this
            });
        });
        console.log(req.session.sharings); //it shows this first
        res.render('home.ejs', {memos: result, sharingsData: req.session.sharings});
    });

    return;
}
res.redirect('/');})

then the problem is that it's rendering the page before looping and getting each memo users (while req.session.sharings is still empty)

login result is :

[ ]    
[{ },{ }] // with my data 

Anyone has any idea of how i can force:

 res.render('home.ejs', {memos: result, sharingsData: req.session.sharings});   

to wait until the end of for each loop !!!!

Drake Perera
  • 49
  • 1
  • 9
  • 1
    Does this answer your question? [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) – VLAZ Mar 06 '20 at 07:59
  • 1
    The other question provides a very good overview of the problem. There is a slight difference in that you don't need to *return* a value but it's logically equivelent - you want a statement to happen *after* an async operation is done. You can `await` or use the Promise API or put the `res.render` as a final call in the callback after `.forEach`. These will ensure that the rendering happens *after* you're done with the operations. – VLAZ Mar 06 '20 at 08:00
  • Also relevant on the topic of asynchronicity, its effects and how to deal with it: [Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference](https://stackoverflow.com/q/23667086) – VLAZ Mar 06 '20 at 08:02
  • Why is this tagged `async`/`await` if you're not using them? – Bergi Mar 06 '20 at 09:01

1 Answers1

0

First of all i'd recommend using Array.prototype.map over a Array.prototype.forEach if you want to return the value from the callback function. Secondly you can create your own Promise that will resolve the moment function call is finished / req.session.sharings is written. I wasn't sure if you really needed to write sharrings manually, so I've decided on just grouping them together and pushing them onto session.sharings array only after they were all collected.

app.get('/home/', (req, res) => {
    let sessionUser = req.session.user;
    if (!sessionUser) return res.redirect('/');

    user.findMemosByUserId(sessionUser.id, result => {
        Promise.all(result.map(memo => {
            return new Promise(resolve => {
                user.findUsersByMemoId(memo.id, result2 => {
                    const obj = {[memo.id]: result2.map(res => res.username)};
                    resolve(obj)
                });
            });
        })).then(sharings => {
            req.session.sharings.push(...sharings);
            console.log(req.session.sharings);
            res.render('home.ejs', {memos: result, sharingsData: req.session.sharings});
        });
    });
});
Krzysztof Krzeszewski
  • 5,912
  • 2
  • 17
  • 30