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
相关产品推荐
相关产品推荐

