You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firebase云函数出现CANCELLED调用取消错误 无法发送FCM推送通知

问题根因

你遇到的两类报错都源于Cloud Function提前终止执行,未等待异步任务完成:

  • CANCELLED: Call cancelled:Function执行到返回语句就被GC回收,后台未完成的Firestore/FCM gRPC调用被直接取消
  • FAILED_PRECONDITION: The requested snapshot version is too old:被挂起的长耗时Firestore查询恢复时,请求的快照版本已超出Firestore缓存有效期

还有两个隐藏问题直接导致通知无法发送:

  • 调用sendNotificationNew时第二个参数test notification未加引号,属于语法错误
  • sendNotificationNew内部没有等待admin.messaging().send()执行完成,所以看不到发送结果日志
修复方案

1. 修复sendNotificationNew异步逻辑

const sendNotificationNew = async (title, body, image, tokens) => {
    console.log("title", title);
    if (!tokens?.length) {
        console.log("Token not available");
        return;
    }
    // 取最后一个token的逻辑保持不变
    if(tokens.length>1){
      tokens = tokens[tokens.length-1];
    }
    try {
      const message = {
        notification: {
          title: title,
          body: body
        },
        token: tokens
      };
     
      if (image) {
        message.notification.image = image;
      }
      // 加await等待FCM发送完成
      const response = await admin.messaging().send(message);
      console.log('Successfully sent message:', response);
      return response;
    } catch (error) {
      console.log('Error sending message:', error);
      console.log('Error sending message:', tokens);
      throw error;
    }
};

2. 修复主函数异步等待逻辑

改掉混合使用async/await和then的混乱写法,收集所有异步任务等待完成后再返回:

const NotificationLive_dayBefore = async () => {
  try {
    const subscribedUsers = await getSubscribedUsers().catch(e => { console.log(e) });
    if (!subscribedUsers?.length) return 'success';
    // 收集所有用户处理任务的Promise
    const taskPromises = [];
    for(const subscriber of subscribedUsers){
      // 每个用户的处理逻辑包装为异步任务
      const userTask = (async () => {
        const snapshot = await db
          .collection('Payments').doc('v1').collection('users')
          .doc(subscriber)
          .collection('subscriptions').where('deleted_at', '==', null)
          .get();
        if (snapshot.empty) return;
        for(const doc of snapshot.docs ){
          if (!doc.exists) {
            console.log("friend_doc_id is not present");
            continue;
          }
          const oneDay = Date.now() + 24 * 60 * 60 * 1000;
          const oneDayMinus1 = Date.now() + 23 * 60 * 60 * 1000;
          const liveLesson = await db
            .collection('Lessons').doc('v1').collection('friends')
            .doc(doc.id)
            .collection('live')
            .where('start_time', '>', new Date(oneDayMinus1))
            .where('start_time', '<', new Date(oneDay))
            .where('deleted_at', '==', null)
            .get();
          for( const liveSnap of liveLesson.docs){
            if (!liveSnap.exists) continue;
            const UserSnapshot = await db
              .collection(models.notification.firebasePath)
              .where('deleted_at', '==', null)
              .where('__name__', '==', subscriber)
              .get();
            for( const userdoc of UserSnapshot.docs){
              const userdocument = userdoc.data();
              const tokens = userdocument.fcm_token || [];
              console.log('tokens',tokens);
              if (!tokens.length) continue;
              let imageURL = null;
              try {
                const live_lessons_images = await db
                  .collection('Accounts').doc('v1').collection('friends')
                  .doc(doc.id)
                  .get();
                imageURL = live_lessons_images.data().image_url || null;
              } catch (error) {
                console.log('获取课程封面失败', error)
              }
              console.log("触发直播课24小时前通知发送");
              // 修复参数未加引号的语法错误
              await sendNotificationNew(
                "test",
                "test notification",
                imageURL,
                tokens
              ).catch(error => { console.error("通知发送失败", error) });
            }
          }
        }
      })();
      taskPromises.push(userTask);
    }
    // 等待所有任务执行完成再返回
    await Promise.all(taskPromises);
    return 'success';
  } catch (err) {
    console.log("执行异常: ", err);
    throw err;
  }
}
额外优化建议
  • 若订阅用户量超过100,建议分批处理任务,避免触发Firestore并发查询限流
  • FCM发送可改用批量发送接口sendAll,减少接口调用次数

内容的提问来源于stack exchange,提问作者Praveen Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 09:51:00