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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 07:48:01