Firebase Firestore 单次事务多次调用transaction.get报错解决方案问询
问题原因分析
- 事务回调的Promise未正确返回:Firestore的
runTransaction要求回调函数返回完整的Promise链,用于告知事务所有操作已完成。你代码中第二个t.get的Promise没有被返回到外层,事务在第一个t.get执行完后就被判定为结束,后续调用t.get时事务已经失效,就会抛出过期错误。 - 异常捕获逻辑无效:
runTransaction本身是异步方法,你没有用await或者.catch处理它的异步异常,外层的同步try/catch根本捕获不到事务内部抛出的错误。 nextFunction调用时机错误:原代码在事务还未执行完成时就提前调用了nextFunction,逻辑时序错误。
正确实现代码
推荐使用更易读的async/await写法,完全符合Firestore事务的语法要求:
import * as admin from "firebase-admin"; export const transactionTest = async function(nextFunction: () => void) { const collection_A_query = admin.firestore().collection("A").limit(499); const collection_B_doc_B1_ref = admin.firestore().collection("B").doc("B1"); try { await admin.firestore().runTransaction(async (t) => { // 所有读操作必须放在所有写操作之前,这是Firestore事务的硬性要求 const coll_A_snapshot = await t.get(collection_A_query); if (coll_A_snapshot.empty) { console.log("coll_A_snapshot is empty"); return; } const coll_B_doc_B1_snapshot = await t.get(collection_B_doc_B1_ref); if (!coll_B_doc_B1_snapshot.exists) { console.log("coll_B_doc_B1_snapshot does not exist"); return; } // 所有写操作放在读操作之后执行 let counter = coll_B_doc_B1_snapshot.get("COUNTER"); for (const doc of coll_A_snapshot.docs) { counter++; t.update(doc.ref, { COUNTER: counter }); } t.update(coll_B_doc_B1_ref, { COUNTER: counter }); }); } catch (e) { console.log("Transaction failure:", e); } finally { // 事务执行完成(无论成功失败)后再调用后续方法 nextFunction(); } };
如果你偏好原始Promise链式写法,只要确保所有嵌套的Promise逐层返回即可:
export const transactionTest = function(nextFunction: () => void) { const collection_A_query = admin.firestore().collection("A").limit(499); const collection_B_doc_B1_ref = admin.firestore().collection("B").doc("B1"); admin.firestore().runTransaction((t) => { // 顶层返回整个Promise链 return t.get(collection_A_query) .then((coll_A_snapshot) => { if (coll_A_snapshot.empty) { console.log("coll_A_snapshot is empty"); return; } // 返回内层t.get的Promise return t.get(collection_B_doc_B1_ref) .then((coll_B_doc_B1_snapshot) => { if (!coll_B_doc_B1_snapshot.exists) { console.log("coll_B_doc_B1_snapshot does not exist"); return; } let counter = coll_B_doc_B1_snapshot.get("COUNTER"); for (let i = 0; i < coll_A_snapshot.docs.length; i++) { counter++; t.update(coll_A_snapshot.docs[i].ref, {COUNTER: counter}); } t.update(coll_B_doc_B1_ref, {COUNTER: counter}); }); }); }) .catch((e) => console.log("Transaction failure:", e)) .finally(() => nextFunction()); };
你之前设想的提前收集所有DocumentReference的方案完全没必要,Firestore事务本身支持多次读操作,只要遵守「所有读操作在写操作之前执行、Promise链正确返回」两个规则即可正常运行。
内容的提问来源于stack exchange,提问作者sisimans
相关产品推荐
相关产品推荐

