1

I was wondering if someone could help enlighten me as to how I can successfully execute this code where it waits until the entire array is filled with FCM tokens before firing sendToDevice().

I've been using these links (listed below) as references to try and resolve this but I still can not figure it out so alas here I am on SO seeking guidance. I just need to pass the tokens array once it is filled completely. I've gotten it where it fired multiple times on each push but never where it asynchronously loads and then fires ><

Firebase Real Time Database Structure for File Upload

Promise.all with Firebase DataSnapshot.forEach

https://aaronczichon.de/2017/03/13/firebase-cloud-functions/

exports.sendVenueAnnouncement = functions.database.ref(`/venueAnnouncements/{venueUid}/announcement`).onCreate(event => {
  const venueUid = event.params.venueUid;
  const announcement = event.data.val();

  const getVenueDisplaynamePromise = admin.database().ref(`verifiedVenues/${venueUid}/displayname`).once('value');
  return getVenueDisplaynamePromise.then(snapshot => {
    const displayname = snapshot.val();
    const payload = {
      notification: {
        title: `${displayname}`,
        body: `${announcement}`
      }
    };
    const getSubscriberTokensPromise = admin.database().ref(`subscribers/${venueUid}`).once('value');
    return getSubscriberTokensPromise.then(snapshot => {
      const tokens = [];
      snapshot.forEach(function(childSnapshot) {
        const key = childSnapshot.key;
        const token = admin.database().ref(`pushTokens/` + key).once('value');
        tokens.push(token);
      });
      return Promise.all(tokens);
    }, function(error) {
      console.log(error.toString())
    }).then(function(values) {
      return admin.messaging().sendToDevice(values, payload).then(response => {
        const tokensToRemove = [];
        response.results.forEach((result, index) => {
          const error = result.error;
          if (error) {
            if (error.code === 'messaging/invalid-registration-token' || error.code === 'messaging/registration-token-not-registered') {
              tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
            }
          }
        });
        return Promise.all(tokensToRemove)
      });
    })
  })
})
KENdi
  • 7,576
  • 2
  • 16
  • 31

1 Answers1

2

You almost have an understanding of Promises. It looks like you're also mixing callbacks with Promises. Firebase and Cloud Functions for Firebase are completely Promise based so there is no need.

With that said, you're code should look something like the following:

exports.sendVenueAnnouncement = functions.database
  .ref(`/venueAnnouncements/${venueUid}/announcement`)
  .onCreate(event => {
    const venueUid = event.params.venueUid
    const announcement = event.data.val()
    let payload
    let tokens = []

    return admin.database()
      .ref(`verifiedVenues/${venueUid}/displayname`)
      .once('value')
      .then(snapshot => {
        const displayname = snapshot.val()
        payload = {
          notification: {
            title: `${displayname}`,
            body: `${announcement}`
          }
        }
        return admin.database().ref(`subscribers/${venueUid}`).once('value')
      })
      .then(snapshot => {
        snapshot.forEach((childSnapshot) => {
          const key = childSnapshot.key
          const token = admin.database().ref(`pushTokens/` + key).once('value')
          tokens.push(token)
        })
        return Promise.all(tokens)
      })
      .then(values => {
        return admin.messaging().sendToDevice(values, payload)
      })
      .then(response => {
        const tokensToRemove = []
        response.results.forEach((result, index) => {
          const error = result.error
          if (error) {
            if (error.code === 'messaging/invalid-registration-token' || error.code === 'messaging/registration-token-not-registered') {
              tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove())
            }
          }
        })
        return Promise.all(tokensToRemove)
      })
  })

Notice I don't assign the Promise to a variable. Just return it and chain a then. A Promise can return another Promise.

I suggest watching this Firecast for a better understanding of Promises.

Cisco
  • 20,972
  • 5
  • 38
  • 60
  • Many thanks, Francisco! Knew I was on the precipice of understanding them, but just couldn't get over the edge. Normally I like to do my best to figure out things on my own, but I reached that dreaded point of hopeless frustration that brought me to SO. Thanks for taking the time to help out! I'll definitely give that Firecast a watch. – AdamJosephNavarro Jul 26 '17 at 16:45
  • Got around to implementing your suggested code and it unfortunately is still resulting in a jumbled array with each push token deeply nested in an object. Similar to what is shown in this link: https://stackoverflow.com/questions/43647534/why-does-returning-snapshot-val-in-a-promise-when-using-promise-all-not-work – AdamJosephNavarro Jul 28 '17 at 02:17