如何防抖异步函数?解决Pinia中Firestore查询重复调用异常
解决Pinia中连续调用Firestore查询方法返回异常的问题
问题根源
你遇到的异常主要来自两个核心问题:
onSnapshot是实时监听机制:每次调用getdayAppointments都会创建一个新的Firestore实时监听,旧监听不会自动销毁。连续调用时,多个监听的回调会无序触发,导致appointments被旧查询结果覆盖。- 异步状态管控缺失:
loadingAppointments的更新和onSnapshot回调的执行顺序不可控,第二次调用设置loading为true后,第一次的回调可能才完成,把appointments重置为旧数据,同时将loading置为false。
解决方案一:使用一次性查询(非实时更新场景)
如果不需要实时同步数据变化,改用getDocs做一次性查询,同时添加请求锁阻止重复调用:
import { defineStore } from 'pinia'; import { collection, getDocs, where, query, addDoc, deleteDoc, doc, updateDoc, } from 'firebase/firestore'; import { db } from '/src/js/firebase'; import { useStoreAuth } from './storeAuth'; // 确保导入Auth Store export const useStoreAppointments = defineStore('storeAppointments', { state: () => { return { loadingAppointments: false, appointments: [], transition: '', dayAppointments: [], selectedDate: today(), isFetching: false, // 新增请求锁状态 }; }, actions: { async getdayAppointments(appointmentdate) { // 正在请求时直接返回,阻止重复调用 if (this.isFetching) return; const storeAuth = useStoreAuth(); this.loadingAppointments = true; this.isFetching = true; try { const appointmentsCollectionRef = collection(db, 'users', storeAuth.user.id, 'appointments'); const q = query(appointmentsCollectionRef, where("appointmentdate", "==", appointmentdate)); const querySnapshot = await getDocs(q); const fbAppointments = []; querySnapshot.forEach((doc) => { fbAppointments.push({ id: doc.id, details: doc.data().details, bgcolor: doc.data().bgcolor, date: doc.data().appointmentdate, duration: doc.data().duration, time: doc.data().time, title: doc.data().patientname, moneypaid: parseInt(doc.data().moneypaid), currencypaid: doc.data().currencypaid, completed: doc.data().completed, doctor: doc.data().doctor, start: doc.data().start, end: doc.data().end, split: doc.data().split, }); }); this.appointments = fbAppointments; } catch (error) { console.error('获取预约数据失败:', error); } finally { this.loadingAppointments = false; this.isFetching = false; // 释放请求锁 } }, }, });
解决方案二:保留实时监听,管理监听生命周期(实时更新场景)
如果需要实时同步数据变化,要保存每次监听的销毁函数,每次调用新查询前取消旧监听:
import { defineStore } from 'pinia'; import { collection, onSnapshot, where, query, addDoc, deleteDoc, doc, updateDoc, } from 'firebase/firestore'; import { db } from '/src/js/firebase'; import { useStoreAuth } from './storeAuth'; export const useStoreAppointments = defineStore('storeAppointments', { state: () => { return { loadingAppointments: false, appointments: [], transition: '', dayAppointments: [], selectedDate: today(), currentUnsubscribe: null, // 保存当前监听的销毁函数 }; }, actions: { getdayAppointments(appointmentdate) { const storeAuth = useStoreAuth(); this.loadingAppointments = true; // 取消之前的监听,避免旧回调干扰 if (this.currentUnsubscribe) { this.currentUnsubscribe(); } const appointmentsCollectionRef = collection(db, 'users', storeAuth.user.id, 'appointments'); const q = query(appointmentsCollectionRef, where("appointmentdate", "==", appointmentdate)); // 保存新的监听销毁函数 this.currentUnsubscribe = onSnapshot(q, (querySnapshot) => { const fbAppointments = []; querySnapshot.forEach((doc) => { fbAppointments.push({ id: doc.id, details: doc.data().details, bgcolor: doc.data().bgcolor, date: doc.data().appointmentdate, duration: doc.data().duration, time: doc.data().time, title: doc.data().patientname, moneypaid: parseInt(doc.data().moneypaid), currencypaid: doc.data().currencypaid, completed: doc.data().completed, doctor: doc.data().doctor, start: doc.data().start, end: doc.data().end, split: doc.data().split, }); }); this.appointments = fbAppointments; this.loadingAppointments = false; }, (error) => { console.error('监听预约数据失败:', error); this.loadingAppointments = false; }); }, // 可选:Store销毁时取消监听,避免内存泄漏 destroy() { if (this.currentUnsubscribe) { this.currentUnsubscribe(); } } }, });
额外优化建议
- 如果是用户快速切换日期导致的连续调用,可以在组件层添加防抖处理(比如用Lodash的
debounce),延迟触发查询,避免短时间内多次请求。 - 确保调用
getdayAppointments时storeAuth.user.id已存在,避免因未登录出现undefined错误。
内容的提问来源于stack exchange,提问作者omar jasim
相关产品推荐
相关产品推荐

