执行Firestore事务时出现Bad state: Future already completed错误怎么办?
问题分析与解决
错误原因
触发Bad state: Future already completed的核心问题有两个:
- 事务内执行了非事务上下文的Firestore操作:你在
runTransaction的回调里调用了ClassifiedAdView.storeCreate,但这个方法直接用FirebaseFirestore.instance执行add操作,绕开了事务对象t。Firestore事务要求所有读写都必须通过事务对象完成,外部操作会打乱事务的状态管理,导致Future状态异常。 - 事务内读取文档的方式错误:你用
fs.collection(...).doc(id).get()读取广告文档,这是事务外的读取操作,无法保证事务的一致性,也会干扰事务的内部状态。
修复步骤
1. 修正事务内的文档读取逻辑
把事务里读取广告文档的代码,改成用事务对象t来读取:
// 原代码 final adDoc = await fs.collection(collectionName).doc(id).get(); // 改为 final adDoc = await t.get(fs.collection(collectionName).doc(id));
2. 修改ClassifiedAdView.storeCreate,支持事务上下文
给storeCreate增加可选的Transaction参数,让它能在事务内执行操作:
static Future<String?> storeCreate({ required ClassifiedAd ad, required User? loggedUser, Transaction? transaction, // 新增参数 }) async { // 跳过自己查看自己广告的情况 if (ad.ownerUid == loggedUser?.id) return null; final fs = FirebaseFirestore.instance; final collectionRef = fs.collection(collectionName); // 根据是否传入事务,选择执行方式 DocumentReference ref; if (transaction != null) { // 事务内操作:用transaction对象创建文档 ref = collectionRef.doc(); await transaction.set( ref, {"adId": ad.id, "userId": loggedUser?.id, "on": Timestamp.now()} ); } else { // 普通操作:直接用Firestore实例 ref = await collectionRef.add({ "adId": ad.id, "userId": loggedUser?.id, "on": Timestamp.now() }); } return ref.id; }
3. 在事务内调用storeCreate时传入事务对象t
修改transactionStoreAddOneToViewCount里的调用代码,把事务对象传进去:
// 原代码 await ClassifiedAdView.storeCreate( ad: this, loggedUser: viewingUser, ); // 改为 await ClassifiedAdView.storeCreate( ad: this, loggedUser: viewingUser, transaction: t, // 传入事务对象 );
4. (可选)验证本地状态更新
你代码里的viewCount += 1是本地状态更新,只要确保这个属性是可监听的(比如用ValueNotifier或对应状态管理方案),这个操作是没问题的,属于事务成功后的本地同步。
最终修复后的transactionStoreAddOneToViewCount代码
Future<void> transactionStoreAddOneToViewCount({required User? viewingUser}) async { final fs = FirebaseFirestore.instance; return fs.runTransaction((t) async { // 用事务对象读取广告文档 final adDoc = await t.get(fs.collection(collectionName).doc(id)); // 事务内更新浏览计数 t.update(adDoc.reference, {"viewCount": FieldValue.increment(1)}); // 传入事务对象,执行添加浏览记录的操作 await ClassifiedAdView.storeCreate( ad: this, loggedUser: viewingUser, transaction: t, ); // 同步本地状态 viewCount += 1; }); }
内容的提问来源于stack exchange,提问作者Stéphane de Luca
相关产品推荐
相关产品推荐

