如何解决嵌套Firebase查询产生的Promise无法及时resolve的问题?
问题诊断
- 错误使用
onSnapshot方法:onSnapshot是 Firestore 的实时监听器,不返回 Promise,await对它完全无效。你的代码会在绑定监听器后立刻执行return Promise.all(test),此时所有用户信息的查询请求还没返回,test数组是空的,自然拿不到数据。 - Promise 收集逻辑错误:你没有把
firestore.collection("users").doc(user).get()返回的 Promise 存入test数组,而是等查询成功后才把会话对象 push 进数组,Promise.all监听的是空数组,会直接 resolve 空结果。 - 内存泄漏风险:
onSnapshot绑定的监听器没有在组件卸载时取消,会导致内存泄漏,如果你只需要单次拉取数据,完全不需要用监听器,改用get()方法即可。
优化实现方案
如果你只需要单次拉取会话和对应用户数据,可以直接使用修正后的代码:
const conversationsRef = firestore.collection("conversations").where("users", "array-contains", auth.currentUser.uid).orderBy("updatedAt", "desc"); const [convo, set] = useState([]); async function getConversations(){ // 单次拉取会话列表,get方法会返回Promise const snap = await conversationsRef.get(); // 收集所有用户信息查询的Promise const userQueryPromises = []; snap.forEach(doc => { const conversation = doc.data(); // 筛选出会话中除当前用户外的目标用户id const targetUserId = conversation.users.find(user => user !== auth.currentUser.uid); if(targetUserId){ // 将查询Promise存入数组,查询完成后返回挂载了用户信息的会话对象 const userQuery = firestore.collection("users").doc(targetUserId).get().then(userProfile => { conversation.po = userProfile.data(); return conversation; }); userQueryPromises.push(userQuery); } }); // 等待所有用户查询完成后返回完整会话列表 return Promise.all(userQueryPromises); } useEffect(()=>{ getConversations().then(data => { console.log(data); console.log(data[0]?.hostId); setActiveChat(data[0]); set(data); }); },[])
如果需要保持会话的实时更新,可以使用监听器版本的实现,同时添加卸载逻辑避免内存泄漏:
useEffect(() => { // 绑定实时监听器,返回取消监听的函数 const unsubscribe = conversationsRef.onSnapshot(async snap => { const userQueryPromises = []; snap.forEach(doc => { const conversation = doc.data(); const targetUserId = conversation.users.find(user => user !== auth.currentUser.uid); if(targetUserId){ const userQuery = firestore.collection("users").doc(targetUserId).get().then(userProfile => { conversation.po = userProfile.data(); return conversation; }); userQueryPromises.push(userQuery); } }); const data = await Promise.all(userQueryPromises); setActiveChat(data[0]); set(data); }); // 组件卸载时取消监听 return () => unsubscribe(); }, [])
内容的提问来源于stack exchange,提问作者Markwin
相关产品推荐
相关产品推荐

