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

Firebase多图上传完成后写入数据库的异步执行顺序异常问题

问题解决方案

问题根源

你的代码没有等待所有异步上传任务执行完成就触发了入库逻辑:forEach 循环本身不支持等待异步任务,内部的 uploadImage、ref.put 都是异步Promise,执行时不会阻塞主线程,所以还没等所有图片上传完成,代码就已经走到了调用 loadData2 的步骤。

修复方案

我们可以将所有上传任务封装为Promise存入数组,通过 Promise.all 等待全部上传完成后再执行入库,同时可以直接在上传完成后获取下载链接,不需要再额外查询存储桶,既提升效率也避免误拿到历史遗留的旧图片。

修改后的完整代码

1. 图片上传逻辑改造

const uploadImage = async (uri) => {
  return new Promise((resolve, reject) => {
    let xhr = new XMLHttpRequest();
    xhr.onerror = reject;
    xhr.onreadystatechange = () => {
      if (xhr.readyState === 4) {
        resolve(xhr.response);
      }
    };
    xhr.open("GET", uri);
    xhr.responseType = "blob";
    xhr.send();
  });
};

const loadData1 = async () => {
  setLoading(true);
  console.log("pro 1");
  // 把所有上传任务封装为Promise数组
  const uploadTasks = filteredUri.map(async (xUri) => {       
    const imagId = Math.random().toString(36).substr(2, 9);                        
    const blob = await uploadImage(xUri);
    const ref = firebase.storage().ref().child(`images/${userId}/${imagId}`);
    await ref.put(blob);
    // 上传完成直接获取下载链接返回
    const downloadUrl = await ref.getDownloadURL();
    console.log("Images uploaded");
    return downloadUrl;
  });
  // 等待所有图片全部上传完成,拿到所有图片的访问链接
  const allImageUrls = await Promise.all(uploadTasks);
  console.log("pro 2,所有图片上传完成,开始执行入库");
  // 把链接数组传给入库逻辑
  await loadData2(allImageUrls);
};

2. 数据入库逻辑改造

async function loadData2(imageUrls) {
  try {
    // 直接用上传得到的链接写入数据库,不需要再查询存储桶
    await firebase.database().ref(`productos/${userId}`).set({
      id: userId,
      title: productName,
      price: precio,
      description: description,
      avgRating: 5,
      ratings: 5,
      // 如果是单图场景就取第一个链接,多图场景直接存数组即可
      image: imageUrls.length === 1 ? imageUrls[0] : imageUrls,
    });
    console.log("Data uploaded");
    setLoading(false);
    setTimeout(function () {
      Alert.alert(
        "All uploaded",
        "All right",
        [
          {
            text: "Ok",
            onPress: () => {},
          },
        ]
      );
    }, 1000);
    navigation.goBack();
  } catch (error) {
    console.log(error);
    alert("Error, try later");
    // 入库失败,清理已经上传的图片
    const imageRef = firebase.storage().ref().child(`images/${userId}`);
    const listResult = await imageRef.listAll();
    const deleteTasks = listResult.items.map(item => item.delete());
    await Promise.all(deleteTasks);
    console.log("已清理已上传的图片资源");
  }
}

注意事项

  • Promise.all 只要有一个任务失败就会进入catch,符合业务要求:只要有一张图片上传失败就不会执行入库,避免数据不完整
  • 改造后避免了原代码多次调用set覆盖商品数据的问题
  • 如果你的业务只需要单图,直接取imageUrls[0]写入即可,也可以在上传前限制只取第一张图片进行上传

内容的提问来源于stack exchange,提问作者José Manuel Rodríguez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 00:54:03