skip to Main Content

I am using MongoseDB in order to receive some information about an item. When i try to search for it, it finds it with no trouble, but for some reasons this function is not pushing them into my array. I think this might be because of some async functions and that the console.log() is triggered before any item is being pushed in there.

const getOrders = function(allOrders){
    let promise = new Promise((succ, fail)=>{
        let ordersTodisplay = []
        for (let order of allOrders) {
            if (!(order.orderId === null || order.orderItem === null)){
                postMong.findById(order.orderItem, function (err, item) {
                    ordersTodisplay.push(item) 
                })
            }  
        } 
        if(ordersTodisplay.length > 0){
            succ(ordersTodisplay)    
        } else{
            fail("no items")
        }
    })
    return promise
}
router.get('/accountpage',function(req,res){ 
    const userDB = req.session.username
    if (userDB !== undefined && userDB){
        userForm.findOne({ username : userDB }, function (err, user) {
            const userOrders = user.userOrders;
            if (userOrders.length > 1) {
                getOrders(userOrders).then((result)=>{console.log(result)}, (fail)=>{console.log(fail)})
                res.render('../view/accountpage',{username: userDB,orders: itemsToDisplay});
            }
            else{
                res.render('../view/accountpage',{username: userDB,orders: "There are no orders"});
            }
        });
    } else {
        res.redirect("/login")
    }
});

The result is : no items

2

Answers


  1. You have to for the database call to complete and then push the data in the array like this, using async-await:

    const getOrders = function(allOrders){
        let promise = new Promise(async (succ, fail)=>{
            let ordersTodisplay = []
            for (let order of allOrders) {
                if (!(order.orderId === null || order.orderItem === null)){
                    await postMong.findById(order.orderItem, function (err, item) {
                        ordersTodisplay.push(item) 
                    })
                }  
            } 
            if(ordersTodisplay.length > 0){
                succ(ordersTodisplay)    
            } else{
                fail("no items")
            }
        })
        return promise
    }
    
    Login or Signup to reply.
  2. Your code is quite nested and that makes it hard to reason about what is happening.

    To break down your code, you:

    • get a single user that has several order IDs referenced
    • load each order
    • respond with those orders (although you return itemsToDisplay that doesn’t seem to be defined anywhere, so I’m a bit confused)

    I’d try to capture that logical pattern in the code. A good trick is returning early to make the code less nested and interdependent:

    router.get('/accountpage', function(req,res){ 
        const userDB = req.session.username;
        if (!userDB){
            res.redirect("/login");
            return;
        }
    
        loadUserOrders(userDB)
            .then(function(orders) {
                if(orders.length > 0) {
                    res.render('../view/accountpage', {username: userDB,orders: orders});
                    return;
                }
                // Note: consider returning just the empty array here, that already implies no orders
                res.render('../view/accountpage', {username: userDB, orders: "There are no orders"});
            })
            .catch(function(error) {
                //TODO: render error -- case not covered by your code
            });
    });
    
    // Because this is an async function you can now await inside it, meaning no need for the callback syntax for mongoose
    async function loadUserOrders(username) {
        const user = await userForm.findOne({ username: username });
    
        // Promise.all runs in parallel several promises and returns an array containing their results
        // .map here turns the list of userOrders into a list of promises getting each order
        return await Promise.all(user.userOrders.map((userOrder) => postMong.findById(userOrder.orderItem));
    }
    

    Notice how this code highlights that you are not explicitly handling the error case from loading orders.

    You can further simplify this by using something like express-async-handler which will let your endpoint function be async as well:

    const asyncHandler = require('express-async-handler');
    
    router.get('/accountpage', asyncHandler(async function(req,res){ 
        const userDB = req.session.username;
        if (!userDB){
            res.redirect("/login");
            return;
        }
    
        // Note: there is no try-catch around loadUserOrders right now, so errors will result in a 500 courtesy of express-async-handler -- better than before
        const orders = await loadUserOrders(userDB);
        if(orders.length > 0) {
            res.render('../view/accountpage', {username: userDB,orders: orders});
            return;
        }
        // Note: consider returning just the empty array here, that already implies no orders
        res.render('../view/accountpage', {username: userDB, orders: "There are no orders"});
    }));
    

    I think using async/await syntax all the way through leaves the code more consistent and easier to reason about than the mix of callbacks, new Promise and await that was suggested in another answer. In the end, the endpoint is not very complex, and I think the code can reflect that.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search