0

I am struggling to get this working properly. I have this function in my nodejs backend

let data = [];
let lists = [];
var userId = request.params.userId;
var coll = db.collection("forms");
var query = coll.where("formUser", "==", userId);
await query.get().then(function (querySnapshot) {
    querySnapshot.forEach(function (doc) {
        var forms = doc.data();
        var listIDs = doc.data().formLists;

        listIDs.forEach(listId => {
            db.collection("lists").where("listId", "==", listId).get().then(function (snapshot) {
                snapshot.forEach(function (d) {
                    lists.push({ "id": d.data().listId, "text": d.data().listName });
                });
            });
        });
        forms.formLists = lists;
        data.push(forms);
    });
});

The second loop for some reason isn't just working, the result of data is from the first loop and if I put the same function inside my javascript frontend, I get the complete data with the listIDs result.

Any ideas please?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Shina
  • 2,019
  • 2
  • 19
  • 25
  • While [this question](https://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-asynchronous-call) deals with Promises from asynchronous calls, it contains a wealth of information related to your problem. Also take a look at the many linked questions in the side bar. – samthecodingman Oct 22 '21 at 08:46
  • @samthecodingman thanks, I think the problem is not in the return ajax based on the link you shared but rather in my backend function above. I sense I have to do something around Promise.all but not sure how. – Shina Oct 22 '21 at 08:54
  • Here is [another question](https://stackoverflow.com/a/67390226/3068190) with the same solution to your problem. – samthecodingman Oct 22 '21 at 08:56

2 Answers2

0

Adding a single await does not suddenly make all the code under it run synchronously. It's also typically an antipattern to combine await and then as you're doing. I recommend staying away from async / await until you've mastered asynchronous behavior, and sticking to then and explicitly dealing with the promises until then.

If the code you shared run in Cloud Functions, you'll need to return a single promise that resolves once all asynchronous (read and write) operations complete. Since you have a lot of such operations, you'll want to capture all the promises you get from them into an array, and then use Promise.all() for the return value:

var userId = request.params.userId;
var coll = db.collection("forms");
var query = coll.where("formUser", "==", userId);
return query.get().then(function (querySnapshot) { // 
    let promises = []; // 
    querySnapshot.forEach(function (doc) {
        var forms = doc.data();
        var listIDs = doc.data().formLists;

        listIDs.forEach(listId => {
            promises.push(db.collection("lists").where("listId", "==", listId).get()); // 
        });
    });
    return Promise.all(promises).then(function (snapshot) { // 
        return snapshot.map((d) => {
            return { "id": d.data().listId, "text": d.data().listName };
        });
    });
});
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thanks a lot Frank but still unsure how to put this together, I need to append the data to `forms.formLists = lists; data.push(forms);` in each process of the data result and your code above got me lost as the `var forms` seems now unusable. – Shina Oct 22 '21 at 14:46
0

So for those struggling with such complex data structure, here is what I did:

        let data = [];
        let forms = [];
        let lists = [];
        var userId = request.params.userId;
        var coll = db.collection("forms");
        var query = coll.where("formUser", "==", userId);
        await query.get().then(function (querySnapshot) {
            querySnapshot.forEach(function (doc) {
                forms.push(doc.data());
            });
        });

        for (let index = 0; index < forms.length; index++) {
            const element = forms[index];
            var listIDs = element.formLists;
            for (const listId of listIDs) {
                await db.collection("lists").where("listId", "==", listId).get().then(function (snapshot) {
                    snapshot.forEach(function (d) {
                        lists.push({ "id": d.data().listId, "text": d.data().listName });
                    });
                });
            }
            element.formLists = lists;
            data.push(element);
        }
                            
        response.status(200).send(data); // pass your data to UI
Shina
  • 2,019
  • 2
  • 19
  • 25
  • Hey there, might I suggest accepting your own answer so that the question appears as answered and is therefore more visible to those who might be struggling with the data structure, as you suggested? – fabc Oct 25 '21 at 15:40