1

I would like my callback function to wait until ClientProductLinks has been filled before sending it via res.send(). I'm sure there's a simple solution but I'm new to JS and cannot seem to figure it out.

app.all('/:client/listproducts', async function(req, res) {
   const client = req.params.client;
   let clientProductLinks = [];
   const clientRef = await db.collection(client).get();
   const snapshot1 = await clientRef;
   snapshot1.forEach(async function (prod) {
       let prodRef = await db.collection(client).doc(prod.id).collection(prod.id).get();
       let snapshot2 = await prodRef;
       snapshot2.forEach(function (doc) {
           const obj = {
               docID: doc.id,
               docData: doc.data()
           };
           clientProductLinks.push(obj);
       });
   });
   res.send(clientProductLinks);
});

Currently, res.send() sends ClientProductLinks back as an empty array. Any help is greatly appreciated.

swallZ
  • 13
  • 4
  • The way you are using await inside a foreach loop isn't working the way you expect. You will need to collect the promises into an array and actually await all of them together. This might be helpful: https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop – Doug Stevenson Sep 27 '19 at 19:10
  • `Array.forEach` doesn't `await` it's callback. You need to either use a `for..of` loop or `Promise.all`. – nicholaswmin Sep 27 '19 at 19:10
  • Possible duplicate of [Using async/await with a forEach loop](https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop) – nicholaswmin Sep 27 '19 at 19:10

1 Answers1

2

You are expecting synchronous execution (snapshot1.forEach) when really writing asynchronous code.

In this case you should be able to use Promise.all to wait for all your product links.

Something like (untested):

app.all('/:client/listproducts', async function(req, res) {
  const client = req.params.client;
  const snapshot1 = await db.collection(client).get();
  const getProduct = async prod => {
    return new Promise(async (resolve, reject) => {
      try {
        const snapshot2 = await db
          .collection(client)
          .doc(prod.id)
          .collection(prod.id)
          .get();
        const links = [];
        snapshot2.forEach(function(doc) {
          const obj = {
            docID: doc.id,
            docData: doc.data()
          };
          links.push(obj);
        });
        resolve(links);
      } catch (error) {
        reject(error);
      }
    });
  };

  const clientProductLinks = await Promise.all(snapshot1.map(getProduct));
  res.send(clientProductLinks);
});
madflow
  • 7,718
  • 3
  • 39
  • 54
  • Thanks very much. Running this yields ```UnhandledPromiseRejectionWarning: TypeError: snapshot1.map is not a function```. I had this problem before; for some reason I am allowed to call forEach(), but not map(), on the snapshot arrays. – swallZ Sep 27 '19 at 19:44
  • What is `db`? Mongo DB? – madflow Sep 27 '19 at 19:54
  • It's firestore actually – swallZ Sep 27 '19 at 22:07