0

I have a request that will return restaurant offers

console.log(offers) inside the for loop, returns what am I want (array with objects) but when I send the offers to the client with res.send(offers) return an empty array

what I should to do?

offers: async (req, res) => {
    let orders = await req.models.order.find({
      "customers.customer_id": req.user._id,
    });

    let cashbacks = await req.models.cash_backs
      .find()
      .populate("restaurant_id");

    let offers = [];
    for (let i = 0; i < cashbacks.length; i++) {
      orders.forEach(async (order) => {
        if (order.cash_back_id != cashbacks[i]._id) {
          await req.models.order
            .find({
              "customers.customer_id": req.user._id,
              cash_back_id: cashbacks[i]._id,
            })
            .then((result) => {
              let allowCount = cashbacks[i].per_visit.values.length;

              if (allowCount > result.length) {
                offers = [...offers, cashbacks[i]];
                console.log(offers); // return [{..}, {..}, {..}]
              } else {
                return;
              }
            });
        }
      });
    }

    res.send(offers);
  },
Amin Azimi
  • 659
  • 9
  • 25
  • 1
    Very classic question about how to return the response of an asynchronous call : https://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-asynchronous-call. Also `await` doesn't work inside array methods like `.forEach` or `.map` (it doesn't actually await) – Jeremy Thille Jun 14 '21 at 09:35

1 Answers1

2

This is happening because res.send(offers) isn't waiting for all the async functions in the forEach loop to finish.

If you want to send only after offers is populated with some data, try adjusting your function to use a for/of loop instead of a forEach loop. Because the callback in the forEach loop is in its own scope, the outer function doesn't wait for all the asynchronous functions in the forEach loop to finish. This causes res.send(offers) to run before offers gets populated with data. By changing the loop to a for/of loop, you're making outer function wait for those calls to finish before moving on.

offers: async(req, res) => {
  let orders = await req.models.order.find({
    "customers.customer_id": req.user._id,
  });

  let cashbacks = await req.models.cash_backs
    .find()
    .populate("restaurant_id");

  let offers = [];
  for (let i = 0; i < cashbacks.length; i++) {
    for (const order of orders) {
      if (order.cash_back_id != cashbacks[i]._id) {
        await req.models.order
          .find({
            "customers.customer_id": req.user._id,
            cash_back_id: cashbacks[i]._id,
          })
          .then((result) => {
            let allowCount = cashbacks[i].per_visit.values.length;

            if (allowCount > result.length) {
              offers = [...offers, cashbacks[i]];
              console.log(offers); // return [{..}, {..}, {..}]
            } else {
              return;
            }
          });
      }
    }
  }

  res.send(offers);
},
isaacsan 123
  • 1,045
  • 8
  • 11
  • If you use `async/await`, you should get rid of `.then()` entirely. Either you `await` something, or you use the `.then()` method, not both at the same time. I know it's part of OP's original code, but this should be fixed as well. Otherwise that's a good answer – Jeremy Thille Jun 14 '21 at 09:37
  • @JeremyThille Hmm, I understand that it might be messy perhaps, but it doesn't really change how effective the code is. – isaacsan 123 Jun 14 '21 at 09:40