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

Firebase异步流程问题:插入操作早于去重完成,求解决方案

解决Firebase异步流程问题:确保去重完成后再执行插入操作

这个问题我之前也碰到过,核心就是Firebase异步操作的顺序没控制好——你的步骤3里有一堆异步的get()请求,但代码没等这些请求全部做完就跑去执行插入了,自然会出现去重没完成就插入的情况。我给你整理了一套修复方案,分两部分来改:

一、先把去重逻辑的异步等待修好

原来的代码在遍历已推荐视频集合时,每个视频都发起了一个获取sequenceNumberID的异步请求,但这些请求是并行跑的,你没等它们全部返回就调用了insertData。这里要用Promise.all来收集所有异步任务,等全部完成后再做去重。

修改后的generateRecommendationsFromGame代码:

export const generateRecommendationsFromGame = (participantID) => {
  let newRec = [];
  const userRef = firebase.firestore().collection("users").doc(participantID);

  // 步骤1:获取游戏输入生成待推荐数组
  return firebase.firestore().collection("inputGame")
    .where("user", "==", userRef)
    .get() // 这里改用get()而非onSnapshot,因为你只需要一次性获取数据,不需要实时监听
    .then(querySnapshot => {
      querySnapshot.forEach(doc => {
        const { consagua, enfartamento, mastigacao, mexdormir, emocoes } = doc.data();

        // 生成待推荐视频序列
        if (Number(consagua) >= 3) newRec.push('V2.3.2');
        if (Number(enfartamento) >= 25) newRec.push('V3.1');
        if (Number(mastigacao) >= 40) newRec.push('V3.2');
        if (Number(mexdormir) >= 3) {
          newRec.push('V3.3');
          newRec.push('V3.4');
        }
        if (Number(emocoes) >= 2) newRec.push('V3.5');
      });

      // 步骤2:获取用户已推荐的视频集合
      return firebase.firestore().collection("recommendedVideo")
        .where("user", "==", userRef)
        .get();
    })
    .then(result2 => {
      // 收集所有获取视频sequence的异步请求
      const sequencePromises = result2.docs.map(doc2 => {
        const videoRefId = doc2.data().video.id;
        return firebase.firestore().collection("videos").doc(videoRefId)
          .get()
          .then(videoDoc => {
            return videoDoc.exists ? videoDoc.data().sequenceNumberID : null;
          });
      });

      // 等待所有sequence获取完成
      return Promise.all(sequencePromises);
    })
    .then(existingSequences => {
      // 步骤3:去重——过滤掉已存在的序列
      const validSequences = existingSequences.filter(s => s !== null).map(String);
      const filteredRec = newRec.filter(rec => !validSequences.includes(rec));

      // 步骤4:确保去重完成后调用插入函数
      return insertData(filteredRec, participantID);
    })
    .catch(error => {
      console.error("处理流程出错:", error);
      alert("操作失败,请重试");
    });
};

这里的关键改进:

  • 用map替代forEach收集异步任务,配合Promise.all等待所有请求完成
  • 把嵌套的异步逻辑改成链式then(),避免回调地狱
  • 统一处理去重逻辑,确保所有已推荐序列都被收集后再过滤待推荐数组

二、修复insertData函数的问题

原来的insertData有几个明显的bug,比如把数组用split(',')拆分(完全没必要),还有没传入participantID导致无法正确关联用户,我一起给你改了:

export const insertData = (arr, participantID) => {
  const userRef = firebase.firestore().collection('users').doc(participantID);

  // 收集所有插入操作的Promise
  const insertPromises = arr.map(sequence => {
    return firebase.firestore().collection("videos")
      .where("sequenceNumberID", "==", sequence.toString())
      .get()
      .then(querySnapshot => {
        if (!querySnapshot.empty) {
          const videoDoc = querySnapshot.docs[0];
          // 插入到recommendedVideo集合
          return firebase.firestore().collection("recommendedVideo").add({
            seen: false,
            user: userRef,
            video: videoDoc.ref
          });
        } else {
          console.log("未找到对应序列的视频:", sequence);
          return Promise.resolve(); // 跳过不存在的视频,避免中断流程
        }
      });
  });

  // 等待所有插入操作完成
  return Promise.all(insertPromises)
    .then(() => {
      console.log("所有推荐视频插入完成");
    })
    .catch(error => {
      console.error("插入数据出错:", error);
      alert(error.message);
    });
};

这里的改进:

  • 接收participantID参数,正确创建用户引用
  • 用Promise.all批量处理插入操作,确保所有插入都被跟踪
  • 替换onSnapshot为get(),避免不必要的实时监听
  • 处理视频不存在的情况,避免整个流程因为单个错误中断

最后再提两个关键注意点

  1. 区分get()和onSnapshot():一次性获取数据用get(),需要实时监听数据变化才用onSnapshot(),不要混用
  2. 始终处理错误:异步流程中一定要加catch(),不然出了问题你根本不知道哪里错了

内容的提问来源于stack exchange,提问作者Ricardo Caldas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:20:57