Firebase异步数据库调用问题:第二次请求始终最后完成
解决Firebase两次异步请求顺序问题
嘿,我猜你遇到的是异步操作未正确等待的问题——Firebase的数据库请求全都是异步的,当你先拿用户的账户编号,再循环发起账户详情请求时,这些详情请求会并行跑,但你的代码大概率没等所有请求完成就dispatch了结果,导致最后完成的请求数据没被包含到最终的数组里。
先帮你还原下问题代码(补全你没写完的部分)
应该是类似这样的逻辑对吧?
export const fetchAccounts = () => { return (dispatch, getState) => { const accounts = []; const uid = getState().auth.uid; // 第一次请求:拉取当前用户的账户ID列表 return firebase.database().ref(`users/${uid}/accountIds`).once('value') .then(snapshot => { const accountIds = snapshot.val(); // 循环发起第二次请求:拉取每个账户的完整数据 for (const id of accountIds) { firebase.database().ref(`accounts/${id}`).once('value') .then(accountSnapshot => { accounts.push(accountSnapshot.val()); }); } // 这里会直接执行dispatch,但此时accounts数组还没被填满! dispatch({ type: 'FETCH_ACCOUNTS_SUCCESS', payload: accounts }); }); }; };
问题出在:循环里的每个账户详情请求都是异步的,.then里的push操作会在之后才触发,但你在循环结束后立刻就dispatch了,这时候数组里可能只有部分数据,甚至是空的,最后完成的请求数据会在dispatch之后才被加进去,所以你会看到第二次请求“始终最后完成”的现象。
解决方案:用Promise.all等待所有异步请求完成
把所有账户详情的请求打包成Promise数组,用Promise.all等所有请求都跑完再dispatch结果,就能解决顺序问题:
export const fetchAccounts = () => { return (dispatch, getState) => { const uid = getState().auth.uid; // 第一步:先拉取用户的账户ID列表 return firebase.database().ref(`users/${uid}/accountIds`).once('value') .then(accountIdsSnapshot => { const accountIds = accountIdsSnapshot.val(); // 处理用户没有账户的情况,直接返回空数组 if (!accountIds) { dispatch({ type: 'FETCH_ACCOUNTS_SUCCESS', payload: [] }); return; } // 把每个账户ID的请求转换成Promise,收集到数组里 const accountPromises = Object.values(accountIds).map(accountId => { return firebase.database().ref(`accounts/${accountId}`).once('value') .then(accountSnapshot => accountSnapshot.val()); }); // 等待所有账户请求都完成,再进入下一步 return Promise.all(accountPromises); }) .then(accounts => { // 所有请求都完成了,现在dispatch完整的账户数组 dispatch({ type: 'FETCH_ACCOUNTS_SUCCESS', payload: accounts }); }) .catch(error => { // 别忘了加错误处理,避免请求失败时没有反馈 dispatch({ type: 'FETCH_ACCOUNTS_FAILURE', payload: error.message }); }); }; };
几个关键细节说明
Promise.all会等到传入的所有Promise都成功resolve后,才会执行下一个.then,这时候拿到的accounts就是所有账户的完整数据数组。- Firebase返回的
accountIds一般是对象格式,所以用Object.values(accountIds)把它转成数组,方便遍历。 - 加了空值判断,如果用户没有任何账户,直接dispatch空数组,避免后续代码报错。
- 补充了错误捕获,请求失败时能dispatch错误状态,方便前端处理异常。
这样修改后,就能确保所有第二次请求都完成后,才会分发包含完整数据的action,完美解决你遇到的问题~
内容的提问来源于stack exchange,提问作者Toesmash
相关产品推荐
相关产品推荐

