React中Promise未等待问题:Firebase多图上传回调提前执行
问题分析与修复方案
核心错误点
- 未将上传操作包装为Promise:
uploadBytesResumable是基于回调的API,你没有把它包装成Promise,导致file.map返回的是undefined数组,Promise.all会直接立即resolve,所以then块会马上执行,data自然是全undefined的数组(你看到的NULL应该是这个数组的输出表现)。 - 文件名重复风险:循环中使用
new Date()生成文件名,由于循环执行速度极快,多个文件会生成相同的文件名,导致后上传的文件覆盖前一个。 - useState更新的闭包问题:
setImgUrl([...imgUrl, downloadURL])依赖当前imgUrl的快照,批量上传时可能丢失之前的URL。
修复后的代码
const uploadImg = async () => { setUploadIsDone(false); if (file.length) { try { const downloadUrls = await asyncFuncUploadToMongoDb(); console.log(downloadUrls); // 所有上传完成后输出URL数组 setProgress(0); setUploadIsDone(true); } catch (error) { console.error("上传失败:", error); // 这里可以添加错误提示逻辑 } } }; const asyncFuncUploadToMongoDb = async () => { const storage = getStorage(); // 为每个文件创建一个Promise,包装上传流程 const uploadPromises = file.map((f, index) => { // 生成唯一文件名:日期+索引+原文件名后缀,避免覆盖 const fileName = `articelImg/${new Date().getTime()}_${index}_${f.name}`; const articelImagesRef = ref(storage, fileName); const uploadTask = uploadBytesResumable(articelImagesRef, f); return new Promise((resolve, reject) => { uploadTask.on( "state_changed", (snapshot) => { const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; setProgress(progress); switch (snapshot.state) { case "paused": console.log("上传已暂停"); break; case "running": console.log("上传中"); break; } }, (error) => { console.error("单个文件上传失败:", error); reject(error); }, async () => { try { const downloadURL = await getDownloadURL(uploadTask.snapshot.ref); resolve(downloadURL); // 将下载URL作为Promise的结果返回 } catch (urlError) { console.error("获取下载URL失败:", urlError); reject(urlError); } } ); }); }); // 等待所有上传Promise完成,返回所有下载URL的数组 const allDownloadUrls = await Promise.all(uploadPromises); // 使用函数式更新更新imgUrl,避免闭包问题 setImgUrl(prev => [...prev, ...allDownloadUrls]); return allDownloadUrls; };
关键修复说明
- 包装上传为Promise:每个文件的上传流程被包裹在
new Promise中,只有当上传完成并获取到下载URL后才resolve,出错时reject,确保Promise.all能正确等待所有上传完成。 - 唯一文件名:用
new Date().getTime()(时间戳)+ 文件索引 + 原文件名生成唯一路径,彻底避免文件覆盖。 - useState函数式更新:
setImgUrl(prev => [...prev, ...allDownloadUrls])使用上一轮的状态快照更新,解决批量上传时的闭包问题。 - 错误处理:添加
try/catch捕获上传过程中的错误,避免程序崩溃。
内容的提问来源于stack exchange,提问作者Captai-N
相关产品推荐
相关产品推荐

