1

I have a function that maps each element of an array (email addresses in this case), yielding an array of promises that should resolve to document ids automatically generated by Firebase (i.e. hAvJ3qPq821tq1q2rrEv, 0tjeKB1aW8jsOAse5fcP).

async function addMultipleParticipants(participant_emails: Array<string>) {
    console.log("Parallelizing participant processing");
    const promises = participant_emails.map(addParticipant);
    const document_ids = await Promise.all(promises);
    console.log("Final document ids: " + document_ids);
    return document_ids;
};

Here is the function that returns the document ids, depending on whether it could find an existing document associated with the email address or needed to create a new document.

async function addParticipant(email_address: string) {
    try {
        console.log("Querying for person");
        const query = await db.collection('people')
            .where('emails', 'array-contains', email_address)
            .limit(1);
        const querySnapshot = await query.get();
        if (!querySnapshot.empty) {
            console.log("Document exists for email " + email_address);
            // TODO: There is only one, so we shouldn't have to iterate
            querySnapshot.forEach(function(docRef: any) {
                console.log("New document id: " + docRef.id);
                const document_id = docRef.id;
                return document_id;
            });
        } else {
            console.log("Creating person with " + email_address);
            const fields = {emails: [email_address]};
            try {
                const docRef = await db.collection('people').add(fields);
                console.log("New document id: " + docRef.id);
                const document_id = docRef.id;
                return document_id;
            } catch (err) {
                console.log("Error adding document:", err);
            }
        }
    } catch (err) {
        console.log("Error getting document:", err);
    }
};

When all participant emails don't exist in documents yet, the functions work as expected, and console.log() outputs Final document ids: hAvJ3qPq821tq1q2rrEv, 0tjeKB1aW8jsOAse5fcP.

However, when at least one email address is associated with an existing document, the promises from addParticipant() do not resolve to anything, and console.log() outputs Final document ids: ,.

In this scenario, what do I need to do to ensure that the array of promises resolves properly in addMultipleParticipants()?

3 Answers 3

1

When all participant emails don't exist in documents yet, the functions work as expected, and console.log() outputs Final document ids: hAvJ3qPq821tq1q2rrEv, 0tjeKB1aW8jsOAse5fcP

This is because it resolves to the following return statement:

return document_id;

However, when at least one email address is associated with an existing document, the promises from addParticipant() do not resolve to anything

Because it doesn't return anything. The last statement you have is :

        // TODO: There is only one, so we shouldn't have to iterate
        querySnapshot.forEach(function(docRef: any) {
            console.log("New document id: " + docRef.id);
            const document_id = docRef.id;
            return document_id;
        });

It is not a return of any id. It is simply a forEach. Example fix would be like:

        // Return the id
        return querySnapshot[0].document_id;
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, seems promising! (No pun intended.) However, when I tried return querySnapshot[0].document_id, it caused an error (TypeError: Cannot read property 'document_id' of undefined). I should point out that document_id is not a field in the document - what I really want is the id of a specific document in the people collection.
I also tried return querySnapshot.getDocuments()[0].getId();, based on Google's documentation, but it threw this error: TypeError: querySnapshot.getDocuments is not a function.
1

With Promise.all if even 1 promise fails, you won't get the result of either. Take the following example:

const get = async () => {
    return Promise.all([
        new Promise((res, rej) => {
            setTimeout(() => {
                res('data!')
            }, 1000)
        }),
        new Promise((res, rej) => {
            setTimeout(() => {
                rej('error!')
            }, 2000)
        })
    ]);
}

const start = async () => {
    const response = await get()
    console.log(response)
}

start()

You will just get an Uncaught (in promise) error! error.

Now if you want to get around this to get at least something back, you could potentially use catch for each promise like so:

const get = async () => {
    return Promise.all([
        new Promise((res, rej) => {
            setTimeout(() => {
                res('data!')
            }, 1000)
        }).catch(err => {
            return null
        }),
        new Promise((res, rej) => {
            setTimeout(() => {
                rej('error!')
            }, 2000)
        }).catch(err => {
            return null
        })
    ]);
}

const start = async () => {
    const response = await get()
    console.log(response)
}

start()

This will then give you ["data!", null] and you can handle the null as you see fit.

Comments

1

Figured it out with some hints from @basarat. There should be an explicit return for the first result, rather than a for-loop: return querySnapshot.docs[0].id;. The full modified function is below:

async function addParticipant(email_address: string) {
    try {
        console.log("Querying for person");
        const query = await db.collection('people')
            .where('emails', 'array-contains', email_address)
            .limit(1);
        const querySnapshot = await query.get();
        if (!querySnapshot.empty) {
            console.log("Document exists for email " + email_address);
            return querySnapshot.docs[0].id;
        } else {
            console.log("Creating person with " + email_address);
            const fields = {emails: [email_address]};
            try {
                const docRef = await db.collection('people').add(fields);
                console.log("New document id: " + docRef.id);
                const document_id = docRef.id;
                return document_id;
            } catch (err) {
                console.log("Error adding document:", err);
            }
        }
    } catch (err) {
        console.log("Error getting document:", err);
    }
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.