i want to return array of posts, but i got an empty array

postsArr does not get data

  router.get('/user-post/:id', checkJwt, (req, res, next) => {
  let postsArr = []
  db.userSchema.findOne({ _id: req.params.id })
    .populate('posts')
    .exec((err, da) => {
      for (let i = 0; i < da.posts.length; i++) {
        db.postSchema.find({ _id: da.posts[i]._id })
          .populate('comments')
          .exec((err, post) => {
            postsArr.push(post)
          })
      }
      console.log(postsArr)
    })

})

Answers 1

  • This is a whole lot easier if you use the promise interface on your database:

    router.get('/user-post/:id', checkJwt, async (req, res, next) => {
        try {
            let da = await db.userSchema.findOne({ _id: req.params.id }).populate('posts').exec();
            let postsArray = await Promise.all(da.posts.map(post => {
                return db.postSchema.find({ _id: post._id }).populate('comments').exec();
            }));
            res.json(postsArray);
        } catch (e) {
            console.log(e);
            res.sendStatus(500):
        }
    });
    

    The challenge with an asynchronous operation in a loop is that they don't run sequentially - they all run in parallel. The for loop just starts all your asynchronous operations and then you never know when they are all done unless you track them all somehow. That can be done without promises by using counters to keep track of when every single asynchronous result is done, but it's a whole lot easier to just let Promise.all() do that for you. It will also put all the results in the right order for you too.


    If you wanted to sequence the database operations and run them serially one at a time, you could do this:

    router.get('/user-post/:id', checkJwt, async (req, res, next) => {
        try {
            let da = await db.userSchema.findOne({ _id: req.params.id }).populate('posts').exec();
            let postsArray = [];
            for (let post of da.posts) {
                let result = await db.postSchema.find({ _id: post._id }).populate('comments').exec();
                postsArray.push(result);
            }
            res.json(postsArray);
        } catch (e) {
            console.log(e);
            res.sendStatus(500):
        }
    });
    

    This second version runs only one database operation at a time, sequentially. It will put less peak load on the database, but likely be slower to finish.


    You will notice that the use of promises and await makes the error handling much simpler too as all errors will propagate to the same try/catch where you can log the error and send an error response. Your original code did not have error handling on your DB calls.


Related Articles