React项目如何编写异步函数解决Firebase上传未完成就触发API请求问题
问题根源
- 你的代码存在三个核心的异步逻辑问题:
forEach是同步遍历逻辑,不会等待Firebase上传任务执行完成,遍历结束后会立刻执行axios.post,此时大部分上传任务还在进行中- React的
setSong是异步状态更新方法,调用后无法立刻读取到最新的song状态值 - Firebase的
uploadTask.on回调是事件触发式的异步逻辑,没有封装为Promise无法直接用async/await等待执行结果
修复后的代码
// 将save改为异步函数 const save = async (items) => { try { // 封装单个文件上传逻辑为Promise const uploadPromises = items.map(async (item) => { const fileName = new Date().getTime() + item.label + item.file.name; const uploadTask = storage.ref(`/items/${fileName}`).put(item.file); return new Promise((resolve, reject) => { uploadTask.on( "state_changed", (snapshot) => { const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; console.log(`Upload is ${progress}% done for ${item.label}`); }, (err) => { console.log(err); reject(err); // 上传失败抛出错误 }, async () => { // 上传完成获取下载链接 const url = await storage.ref("items").child(fileName).getDownloadURL(); // 返回当前文件对应的label和url resolve({ label: item.label, url }); } ); }); }); // 等待所有文件上传完成,获取所有文件的url映射 const uploadResults = await Promise.all(uploadPromises); // 直接构造完整的提交数据,不依赖异步更新的song状态 const finalSongData = { ...song }; uploadResults.forEach(({ label, url }) => { finalSongData[label] = url; }); // 所有资源准备完成后再发起POST请求 const res = await axios.post("songs/create", finalSongData); console.log('提交成功', res); // 如果需要更新本地state可以在这里执行 setSong(finalSongData); } catch (err) { // 统一捕获上传或者接口请求的错误 console.error('操作失败', err); } }
关键修改说明
- 将单个文件的上传逻辑封装为Promise,成功时返回对应资源的label和访问地址,失败时抛出错误
- 用
Promise.all等待所有上传任务全部完成后,再构造最终的提交数据,避免提前发起接口请求 - 不依赖异步更新的
song状态读取值,直接基于原song和上传结果构造完整的提交数据,避免状态更新延迟导致的数据缺失 - 统一增加错误捕获逻辑,避免某一步异常后没有兜底处理
内容的提问来源于stack exchange,提问作者Nivin Viraj
相关产品推荐
相关产品推荐

