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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 08:54:19