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

等待JS函数执行完成获取响应 修复Firebase上传URL未就绪执行后续逻辑问题

问题根源

你写的uploadFile本身没有返回Promise,uploadTask.on()是基于事件回调的异步API,函数执行到监听绑定那步就会同步结束返回,你在循环里写的await uploadFile()根本等不到上传完成、拿到下载链接的流程走完,自然会出现后续逻辑提前跑、URL没收集全的问题。

修复方案

1. 改造uploadFile为Promise风格

把上传逻辑包进Promise,单张图上传成功拿到URL就resolve,报错就reject,这样await才能真正等待上传流程结束。不要在上传函数里直接修改外部传入的data对象,直接把拿到的URL返回即可,避免不可控的副作用:

const uploadFile = useCallback((file) => {
  if (!file) return Promise.reject(new Error('无效上传文件'));

  const storageRef = ref(storage, `/images/${file.name}`);
  const uploadTask = uploadBytesResumable(storageRef, file);

  return new Promise((resolve, reject) => {
    uploadTask.on(
      "state_changed",
      (snapshot) => {
        // 需要做上传进度条就在这里计算进度,不需要可以留空
      },
      (err) => {
        console.error('图片上传失败:', err);
        reject(err);
      },
      async () => {
        try {
          const downloadUrl = await getDownloadURL(uploadTask.snapshot.ref);
          resolve(downloadUrl);
        } catch (err) {
          reject(err);
        }
      }
    );
  })
}, []);

2. 调整提交逻辑,统一收集所有URL后再执行后续操作

等所有图片上传流程全部结束、拿到完整的URL数组之后,再组装数据上传到Firestore,同时修正原来的判断顺序问题,加上异常捕获和loading状态的统一处理:

const handleFormSubmit = useCallback(
  async (data) => {
    setLoading(true);
    try {
      let downloadedUrl = [];

      if (data?.image?.length) {
        // 并行上传所有图片,速度更快;需要顺序上传的话看下面的替代写法
        downloadedUrl = await Promise.all(
          data.image.map(img => uploadFile(img))
        );

        // 顺序上传替代写法(单张传完再传下一张):
        // for (const img of data.image) {
        //   const url = await uploadFile(img);
        //   downloadedUrl.push(url);
        // }
      }

      if (!downloadedUrl.length) {
        dispatch(handleAlertState({ message: "Error Occured!!!" }));
        router.push("/services");
        return;
      }

      const submitData = {
        ...data,
        downloadedUrl
      };

      await uploadDataToFirestore(submitData);
      router.push("/");
    } catch (err) {
      console.error('提交失败:', err);
      dispatch(handleAlertState({ message: "Error Occured!!!" }));
      router.push("/services");
    } finally {
      // 不管成功失败都关闭loading状态
      setLoading(false);
    }
  },
  [dispatch, router, uploadDataToFirestore, uploadFile]
);

注意点

  • 用Promise.all做并行上传的话,只要有一张图上传失败就会直接进入catch分支,适合要求所有图片都必须上传成功的场景
  • 原来在回调函数里直接push外部数组的写法依赖引用修改,异步时序不可控,改成等所有异步任务返回结果后统一组装数据的写法,能彻底避免URL收集不全的问题
  • 原来的逻辑里uploadDataToFirestore没有加await,也可能导致路由跳转时firestore上传还没完成,改造后加了await保证流程顺序正确

内容的提问来源于stack exchange,提问作者Chandler Bing

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 19:21:27