node.jsfirebasegoogle-cloud-functionsfirebase-cloud-messagingfirebase-admin

firebase function fails to send push notification with socket disconnect


i am trying to send push notification like below:

const { getFirestore } = require('firebase-admin/firestore');
const { initializeApp } = require('firebase-admin/app');
const { https } = require('firebase-functions/v2')
import { getMessaging } from "firebase-admin/messaging";
const { onRequest } = require("firebase-functions/v2/https");
const { onCall, HttpsError } = require("firebase-functions/v2/https"

initializeApp();
const db = getFirestore();
const messaging = getMessaging();
const auth = getAuth();

  exports.missingDataProgress = onSchedule("every 24 hours", async (event:any) => {
  logger.log("Start of missingDataProgress")

  const dashboardReadyRef = new FieldPath("profile", "dashboardReady")
  const appColRef = db.collection("users"); // best practice to use "ColRef" instead of just "Ref"
  const querySnapshot = await appColRef.where(dashboardReadyRef, '==', false).get()//

   querySnapshot.forEach(async(doc:any) => {
       let data:any 
       data = doc.data()
       //logger.log("v2:found trial and active data as:", data)
       if(data.profile !== undefined && data.profile.pushToken !== undefined && data.profile.pushToken !== '' && data.profile.pushToken !== null){
        const pushPayload = {
          title: "Missing Data!",
          body: genMissingDataMessage(),
          image: "",
        }

        logger.info("push payload is", pushPayload)
                
        const message:any = {
            token: data.profile.pushToken,
            notification: pushPayload,
        }

        try{
            await messaging.send(message);
            logger.info("push message sent")
        }catch(err:any){
            logger.error("failed to send push token", err)
            if(err.code === 'messaging/invalid-registration-token' || err.code === 'messaging/registration-token-not-registered'){
              logger.error("detected no longer valid token so removing it")
              data.profile.pushToken = ''
              const nwColRef = db.collection("users")
              await nwColRef.doc(data.profile.uid).update({"profile": data.profile})
            }
        }  
       }
   });

})


Solution

  • You're not dealing with promises correctly for Cloud Functions. Your function must return a promise that resolves only after all other async work is complete. When you use forEach, that "ignores" all of the promises within the lambda you pass to it, and your function returns without waiting for completion, which can cause problems for the abandoned async work.

    See also:

    The error you have is the same error discussed in this other questions, which also were dealing with promises incorrectly:

    Consider converting to a for/of loop instead of forEach, or use Promise.all to create a new promises that resolves only after the provided list of promises resolve. This has been discussed in detail in other questions: