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(),避免不必要的实时监听 - 处理视频不存在的情况,避免整个流程因为单个错误中断
最后再提两个关键注意点
- 区分
get()和onSnapshot():一次性获取数据用get(),需要实时监听数据变化才用onSnapshot(),不要混用 - 始终处理错误:异步流程中一定要加
catch(),不然出了问题你根本不知道哪里错了
内容的提问来源于stack exchange,提问作者Ricardo Caldas
相关产品推荐
相关产品推荐

