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
相关产品推荐
相关产品推荐

