Collect results from firebase cloud function async call in a loop

I am trying to query firebase data in a loop, then I need to collect data from there create a collection of data and return to the calling function.

Below is my code, and I am unable to collect results and return statement gets executed before all results are collected in the array.

function xyz() {
    ...
    // variable lookupKey is defined above this function
    return dbRef.child(`/location/${lookupKey}`).once('value').then(contacts => {
        const otherUserNotificationids = []
        contacts.forEach(function(element) {
            var id = element.val()
            console.log(`found id: ${id}`)
            dbRef.child(`/tokenLookup/${id}`).once('value').then(notifId => {
                const nVal = notifId.val()
                console.log(`found notification id : ${nVal}`)
                otherUserNotificationids.push(nVal)
            })
        });
        const len = otherUserNotificationids.length
        console.log(`found notificationIds count : ${len}`)
        return Promise.all([otherUserNotificationids])
    })
}

As expected the function finishes with 'found notificationIds count : 0',

How do I collect the results from the call to /tokenLookup/${id} and create a collection out of them.

I am doing this, so I do not have to call admin.messasing().sendToDevice() for every token, instead I can just call it once with a array to tokens and its done.

After Doug's response, thought hard about my use of Promises and this is how i resolved it:

function xyz() {
   //moved this variable to top of function (rather then inner scope)
   const otherUserNotificationids = []    
    ...
    // variable lookupKey is defined above this function
    return dbRef.child(`/location/${lookupKey}`).once('value').then(contacts => {
        const promises = []
        contacts.forEach(function(element) {
            var id = element.val()
            console.log(`found id: ${id}`)
            const prms = dbRef.child(`/tokenLookup/${id}`).once('value').then(notifId => {
                const nVal = notifId.val()
                console.log(`found notification id : ${nVal}`)
                otherUserNotificationids.push(nVal)
            })
            promises.push(prms)
        });
        return Promise.all(promises).then(res => {
        const len = otherUserNotificationids.length
        console.log(`found notificationIds count : ${len}`)
        return Promise.all([otherUserNotificationids])
        })
    })
}

Upvotes: 0

Views: 449

Answers (1)

Doug Stevenson
Doug Stevenson

Reputation: 317948

This call is asynchronous and returns immediately:

dbRef.child(`/tokenLookup/${id}`).once('value').then(...)

And you're not waiting for it to finish before returning the array otherUserNotificationids.

Also, it seems you've made a misunderstanding about what to pass to Promise.all(). Promise.all() accepts an array of promises to wait on before resolving the promise that it returns, which is not what you're doing.

What should do instead is collect all the promises returned by the repeated calls to once() into an array, pass the array to Promise.all(), then after its returned promise resolves, deal with all the snapshots that becomes available in the callback.

Upvotes: 2

Related Questions