为何我的Firebase Cloud Function会执行两次?
Firebase Cloud Function生成验证码时被触发两次的问题
我使用Firebase Cloud Function生成一个4位数字格式的验证码,但在Firebase控制台中发现该函数被调用了两次。
触发函数代码
const requestCode = async (uid) => { const ref = doc(FIREBASE_FIRESTORE, "codes", uid); const data = { venue_id: venueID, offer_id: offerID, status: "pending", created_at: serverTimestamp(), code: null, }; await setDoc(ref, data, { merge: true }); listenForCode(uid); };
生成验证码的函数代码
function generateRandomNumbers() { const numbers = []; for (let i = 0; i < 4; i++) { numbers.push(Math.floor(Math.random() * 10)); } const id = numbers.join(""); return id; } exports.generateCode = functions.firestore .document(`codes/{uid}`) .onWrite(async (change, context) => { const prevData = change.before.data(); const newData = change.after.data(); const code = generateRandomNumbers(); if (prevData.code === null) { return await change.after.ref.set({ code: code }, { merge: true }); } else { return null; } });
控制台演示

问题原因
onWrite触发器特性:onWrite会在文档创建、更新、删除时均触发。你的流程中:- 第一步:调用
requestCode写入code: null到文档,触发第一次generateCode - 第二步:
generateCode执行set更新code字段,再次触发onWrite
- 第一步:调用
- 条件判断逻辑错误:当文档首次创建时,
change.before.data()返回null,此时访问prevData.code会抛出类型错误,触发Cloud Functions的重试机制;即使未报错,第二次触发时prevData.code为null(首次写入的值),条件依然成立,会再次执行写入操作。
修复方案
方案1:改用onCreate触发器
如果仅需要在文档创建时生成验证码,使用onCreate替代onWrite,避免后续更新触发函数:
exports.generateCode = functions.firestore .document(`codes/{uid}`) .onCreate(async (snap, context) => { const data = snap.data(); if (data.code === null) { const code = generateRandomNumbers(); return await snap.ref.set({ code: code }, { merge: true }); } return null; });
方案2:修正onWrite的条件判断
保留onWrite但优化逻辑,确保仅在需要生成验证码时执行写入,同时处理幂等性:
exports.generateCode = functions.firestore .document(`codes/{uid}`) .onWrite(async (change, context) => { const newData = change.after.data(); // 仅当新数据的code为null,且当前文档未生成过code时执行 if (newData && newData.code === null) { const docSnapshot = await change.after.ref.get(); const currentCode = docSnapshot.data()?.code; if (currentCode === null) { const code = generateRandomNumbers(); return await change.after.ref.set({ code: code }, { merge: true }); } } return null; });
方案3:优化requestCode的写入逻辑
如果是创建新文档,无需使用{ merge: true },直接写入即可,避免不必要的文档更新触发:
const requestCode = async (uid) => { const ref = doc(FIREBASE_FIRESTORE, "codes", uid); const docSnapshot = await getDoc(ref); const data = { venue_id: venueID, offer_id: offerID, status: "pending", created_at: serverTimestamp(), code: null, }; // 仅当文档不存在时创建,存在时按需更新 if (!docSnapshot.exists()) { await setDoc(ref, data); } else { await updateDoc(ref, { status: "pending", created_at: serverTimestamp(), code: null }); } listenForCode(uid); };
内容的提问来源于stack exchange,提问作者David Henry
相关产品推荐
相关产品推荐

