Search code examples
javascriptarraysmongodbasynchronouses6-promise

Unable to resolve promise rejection and send array as response


I am trying to handle two queries within each other.

exports.get_users = (req, res) => {

  SubscriptionPlan.find()
    .then((result) => {
      if (!result) {
        return res.status(400).json({ message: "unable to process" });
      }
      let modifiedData = [];
      result.forEach(async (data) => {
        if (data.processStatus === "active") {
          await Users.findById(data.userId).then(
            (response) => {
              console.log(response)
              modifiedData.push(response);
              res.json(modifiedData)
            }
          );
        }
      });
    })
    .catch((err) => console.log(err));
};

My issue goes, if I follow this approach, then I get the error for promise rejection:

node:15036) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:470:11)

and also, the response of modifiedData array I receive in postman, only a single object out of array of length 3. The statement console.log(response), returns 3 objects that I need to push to new array.

If I change my code like this to resolve the rejection,

exports.get_users = (req, res) => {

  SubscriptionPlan.find()
    .then((result) => {
      if (!result) {
        return res.status(400).json({ message: "unable to process" });
      }
      let modifiedData = [];
      result.forEach(async (data) => {
        if (data.processStatus === "active") {
          await Users.findById(data.userId).then(
            (response) => {
              console.log(response)
              modifiedData.push(response);
            }
          );
        }
      });
      res.json(modifiedData)
    })
    .catch((err) => console.log(err));
};

it returns me an emtpy array as reponse in the postman. I am aware that I am stuck between async nature of JS, but unable to resolve this issue.

PS. Using async-await is not helpful here for me.

Any early help will be appreciated.


Solution

  • result.forEach returns array of promises. You need to promisify all at once using Promise.all([])

    exports.get_users = (req, res) => {
      SubscriptionPlan.find().then(async (result) => {
        if (!result) {
          return res.status(400).json({ message: "unable to process" });
        }
        let modifiedData = [];
        await Promise.all(
          result.map(async(data) => {
            if (data.processStatus === "active") {
              const response = await Users.findById(data.userId);
              modifiedData.push(response);
            }
          })
        );
        return res.json(modifiedData);
      }).catch((err) => console.log(err));
    };
    

    Or can find at once

    exports.get_users = async (req, res) => {
      try {
        const result = await SubscriptionPlan.find({ processStatus: "active" });
        if (!result) {
          return res.status(400).json({ message: "unable to process" });
        }
        const ids = result.map(({ userId }) => userId);
        const response = await Users.find({ userId: { $in: ids } });
        return res.json(response);
      } catch (err) {
        console.log(err)
        return res.status(400).json({ message: "unable to process" });
      }
    };