You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Firebase应用异步调用顺序问题:成员删除逻辑异常

问题描述

我正在为自己的足球队开发一款React应用,用于记录每位成员的缴费与欠款情况。期望实现的逻辑是:先查询各赛季的数据库记录,若未找到该成员的相关记录,则删除该成员。但当前实际执行顺序为:先删除成员,再执行数据库查询。使用Firebase存储所有数据,相关代码如下:

import database from '../firebase/firebase';

export const startRemoveMember = ( playerUuid, seasonList ) => 
{
    return (dispatch, getState) =>
    {
        let canDelete = true;
        const uid = getState().auth.uid;

        // 期望这部分先执行完成
        seasonList.forEach( (season) =>
        {
            database.ref(`subs-tracker/users/${uid}/debts_and_payments/${season.seasonUuid}`)
                .once('value')
                .then((records) =>
                {
                    records.forEach((childRecord) =>
                    {
                        if(childRecord.val().playerUuid === playerUuid)
                        {
                            canDelete = false;
                            return true; // 找到记录就终止循环
                        };
                    });
                });
        });

        // 实际这部分先执行
        if(canDelete)
        {
            alert('Deleted');
            return database.ref(`subs-tracker/users/${uid}/members/${playerUuid}`)
                .remove()
                .then((ref) =>
                {
                    dispatch(removeMember(playerUuid)); // 从应用状态中移除成员
                })
        }
        else
        {
            alert('Cannot Delete. Member has records');
            return false;
        }
    };
};
问题原因

Firebase的once('value')是异步操作,会返回一个Promise。你用forEach遍历赛季发起查询时,这些查询会在后台异步执行,而主线程会直接跳过这部分,先执行后面的if(canDelete)判断——此时canDelete还没被异步回调修改,默认值为true,导致直接执行删除操作。

解决方案

需要用Promise.all()等待所有赛季的查询请求完成后,再判断是否可以删除成员。修改后的代码如下:

import database from '../firebase/firebase';

export const startRemoveMember = (playerUuid, seasonList) => {
    return (dispatch, getState) => {
        const uid = getState().auth.uid;
        
        // 把每个赛季的查询转换成Promise数组
        const checkRecordPromises = seasonList.map(season => {
            return database.ref(`subs-tracker/users/${uid}/debts_and_payments/${season.seasonUuid}`)
                .once('value')
                .then(records => {
                    // 检查当前赛季是否有该成员的记录
                    return records.some(childRecord => childRecord.val().playerUuid === playerUuid);
                });
        });

        // 等待所有查询完成
        return Promise.all(checkRecordPromises)
            .then(results => {
                // 只要有一个赛季存在该成员的记录,就不能删除
                const hasRecords = results.some(result => result === true);
                
                if (!hasRecords) {
                    alert('Deleted');
                    return database.ref(`subs-tracker/users/${uid}/members/${playerUuid}`)
                        .remove()
                        .then(() => {
                            dispatch(removeMember(playerUuid));
                        });
                } else {
                    alert('Cannot Delete. Member has records');
                    return false;
                }
            })
            .catch(error => {
                console.error('Error checking records:', error);
                alert('Error verifying records, please try again');
            });
    };
};

关键修改点

  • 用map替代forEach,将每个赛季的查询转换为Promise并存入数组
  • 用Promise.all()等待所有Promise完成,确保所有查询执行完毕后再进行判断
  • 使用some()方法简化记录存在性判断:只要有一个赛季返回true(存在记录),就标记为不能删除
  • 增加错误捕获,处理查询过程中可能出现的异常

内容的提问来源于stack exchange,提问作者Luke Marvelly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 17:55:18