0

I have 2 mongoose schemas and i am getting data from it , but before the data is resolved from the schemas , the response is being sent.

//I have tried some callback hacks but it doesnt work.

router.get("/api/getPosts", (req, res) => {
  let array = [];

  post.find({}).then(posts => {

    posts.forEach(post => {
      post.likes.forEach(like => {
        dummy.find({ _id: like.likeUserId }).then(user => {
          array.push(user);
        });
      });
    });

  });
  res.send({data: array})

});

I have atleast 2000 post coming from the database. And before the users data who have liked the post is pushed in array, it sends the response and the data comes empty.

Dharmik soni
  • 343
  • 2
  • 16

2 Answers2

2

This is a simple callback issue. When it comes to the line

post.find({}).then(....

The DB call would be asynchronous. It will not wait and immediately invoke,

res.send({data: array})

Thus your response is empty.

Solution

We can solve this by using async/await


    router.get('/api/getPosts', async (req, res) => {
      const array = [];

      const posts = await post.find({});

      for (const postItem of posts) {
        for (const like of postItem.likes) {
          const user = await dummy.find({ _id: like.likeUserId });
          array.push(user);
        }
      }

      res.send({ data: array });
    });

I prefer using async/await, it's more readable and explicitly conveys the purpose.

Hope this helps! :)

Ramaraja
  • 2,526
  • 16
  • 21
  • The async await is waiting for the posts to load but still the the response is sent before the dummy.find and array.push is resolved, so still empty array – Dharmik soni Jul 26 '19 at 11:51
  • @Dharmiksoni I have updated the answer again and removed the plain promise-based approach. ```async/await``` is far better on the eyes. – Ramaraja Jul 26 '19 at 11:56
  • 1
    I read in multiple places you should not use `foreach` at all. It is better to user `Promise.all` or `for of` ... great examples. https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop – jack blank Jul 26 '19 at 12:00
  • Your code shows error that await can only be used inside async functions. – Dharmik soni Jul 26 '19 at 12:04
  • 1
    @jackblank Good point! I merely quickly edited the code to show how we can use ```async/await``` didn't really bother correcting anything else. I have updated the answer with ```for..of``` loop which looks much better. Thank you. @Dharmiksoni Please refer the updated answer with a much cleaner code using ```for...of``` loop. Disclaimer I haven't tested this locally but this should be right – Ramaraja Jul 26 '19 at 12:17
  • 1
    Thanks @RamarajaRamanujan for the help. You saved me man, cz i needed it asap . Again Thanks – Dharmik soni Jul 26 '19 at 17:14
1

You made a common mistake. You are using a foreach loop where the continuation of the execution of the code doesn't wait for the async requests to finish. your async function is probably this dummy.find() There are different methods discussed through out stackoverflow about how to handle this. Most common is to put them in promise.all

something like

//pseudo code
var likePromises = posts.map(post => dummy.find({ _id: post.like.likeUserId }))
Promise.all(likePromises)
    .then(users =>{
        res.send({data : users})
    })

assuming dummy.find is a promise.

might not be exactly what you want but the point is Promise.all() takes a bunch of promises and waits for the results. in this case it is the look up for all the likes in the user.

Another solution is to use async await and for of that stuff is really good at making async look more like sync code.

jack blank
  • 5,073
  • 7
  • 41
  • 73