Firebase Functions调用FCM发推送后返回408超时且未清理无效token问题
问题根因
1. 408超时错误
你在函数最后仅调用了response.status(200),没有执行send()方法,云函数会一直等待响应返回,直到触发默认60秒超时阈值,就会返回408超时。
2. 无效token未清理
forEach循环内的ref.update是异步操作,你没有等待这些异步任务执行完成就返回了响应,云函数返回响应后会快速冻结执行环境,大部分更新操作还没执行完就被终止。- 循环中每次过滤token都基于原始的
deviceTokens数组,如果同时存在多个无效token,每次更新只会删除当前遍历到的token,前序的删除操作会被覆盖,最终只会保留最后一次删除的结果,清理不彻底。 - 你之前的错误处理分支中,调用
response.status(500).send(e)后没有加return,报错后后续代码仍会继续执行,也可能引发逻辑异常。
修复方案
修改后的完整代码如下:
exports.sendMessage = functions.https.onRequest(async (request, response) => { const { sender, recipient, content, docID } = request.body functions.logger.log("docID:", docID); // 获取设备通知token列表 let deviceTokens; let ref; try { ref = admin.firestore().collection("deviceToken").doc(recipient); const doc = await ref.get(); if (!doc.exists) { console.log("文档不存在!"); // 发送响应后加return终止后续执行 return response.status(500).send("文档不存在") } else { console.log("doc.data():", doc.data()); deviceTokens = doc.data().token; } } catch (e) { return response.status(500).send(e) } let senderProfile; try { senderProfile = await admin.auth().getUser(sender); console.log("senderProfile", senderProfile); } catch (e) { console.log(e); return response.status(500).send(e) } // 通知详情 let payload = { notification: { title: senderProfile.displayName, body: content, sound: "default", }, data: { uid: senderProfile.uid, displayName: senderProfile.displayName, docID, messageType: "status" } }; functions.logger.log( "deviceTokens", deviceTokens, "payload", payload, ); // 向所有token发送通知 const messageResponse = await admin.messaging().sendToDevice(deviceTokens, payload); // 先收集所有无效token const invalidTokens = [] messageResponse.results.forEach((result, index) => { const error = result.error; if (error) { functions.logger.error( "向以下地址发送通知失败", deviceTokens[index], error, ); if (error.code === "messaging/invalid-registration-token" || error.code === "messaging/registration-token-not-registered") { invalidTokens.push(deviceTokens[index]) } } }); // 统一清理无效token if (invalidTokens.length > 0) { const updatedTokens = deviceTokens.filter(token => !invalidTokens.includes(token)); console.log("updatedTokens", updatedTokens); try { await ref.update({ token: updatedTokens }) } catch (e) { console.error("移除token出错", e); return response.status(500).send(e) } } // 调用send方法返回响应 return response.status(200).send({ success: true }) });
核心修改点
- 所有
response.send后添加return,避免报错后逻辑继续执行 - 先统一收集所有无效token,一次性过滤完成后仅执行一次Firestore更新操作,避免多次更新覆盖
- 用
await等待token更新操作执行完成后再返回响应 - 最后响应添加
send()方法,主动结束云函数调用,避免超时
内容的提问来源于stack exchange,提问作者Kevvv
相关产品推荐
相关产品推荐

