Firebase+JavaScript更新值丢失求助:快速点击操作未全部记录
问题描述

我开发了一个类似统计面板的网站,点击-1按钮可使游戏分数减1,当前使用JavaScript操作Firebase数据库实现该功能,代码如下:
const docRef = doc(db, "Games", game_id); const docSnap = await getDoc(docRef); var score = docSnap.data().team2_score await updateDoc(docRef, { team2_score: score -= number });
问题在于,若用户快速多次点击按钮,Firebase无法执行所有操作,例如快速点击5次时,数据库仅记录4次更新,特此求助如何确保每次点击操作都能同步到数据库。
解决方法
根本原因
当前代码存在竞态条件:快速点击时,多个请求会同时读取到数据库里的旧分数,然后基于这个旧值计算新分数更新,导致后一次的更新覆盖前一次,丢失部分操作记录。
方法1:使用Firebase原子增量更新(推荐)
Firebase Firestore提供了FieldValue.increment API,能让数据库端原子性地处理数值增减,不需要先读取当前值,从根源避免竞态问题。
代码示例:
import { doc, updateDoc, increment } from "firebase/firestore"; const docRef = doc(db, "Games", game_id); // 传入负数就是减分,比如减1就传-1 await updateDoc(docRef, { team2_score: increment(number) });
方法2:使用事务处理(适合复杂逻辑场景)
如果需要在更新前做额外校验(比如分数不能低于0),可以用事务确保「读取-校验-更新」的原子性,事务会自动重试直到成功。
代码示例:
import { doc, runTransaction } from "firebase/firestore"; const docRef = doc(db, "Games", game_id); await runTransaction(db, async (transaction) => { const docSnap = await transaction.get(docRef); if (!docSnap.exists()) { throw new Error("游戏记录不存在"); } const currentScore = docSnap.data().team2_score; // 示例:限制分数不能为负 if (currentScore + number < 0) { throw new Error("分数不能低于0"); } transaction.update(docRef, { team2_score: currentScore + number }); });
方法3:前端按钮防重复点击
配合前端控制,点击按钮后暂时禁用,直到请求完成再恢复,减少重复请求的触发:
const minusBtn = document.getElementById("minus-score-btn"); minusBtn.addEventListener("click", async () => { minusBtn.disabled = true; try { // 执行更新操作(二选一上面的代码) const docRef = doc(db, "Games", game_id); await updateDoc(docRef, { team2_score: increment(-1) }); } catch (err) { console.error("更新失败:", err); } finally { minusBtn.disabled = false; } });
内容的提问来源于stack exchange,提问作者splurring
相关产品推荐
相关产品推荐

