如何让循环等待Firebase Storage文件上传完成后再执行下一次迭代?
解决Firebase Storage循环上传时未等待完成就退出的问题
这个问题我之前也碰到过!核心原因就是Firebase的上传操作是异步的,但你的for循环是同步执行的——循环会一口气跑完所有迭代,根本不等每个上传任务完成,所以alert("Images Uploaded Successfully")会提前弹出来,甚至上传还在后台运行的时候,循环就已经结束了。
下面给你两种靠谱的解决方案,你可以根据需求选择:
方案一:串行上传(逐个完成再继续)
把上传逻辑包装成返回Promise的函数,再用async/await让循环等待每个上传完成。这种方式适合需要按顺序上传文件,或者文件数量不多的场景。
首先写一个上传单个文件的辅助函数:
async function uploadSingleFile(file, fileName, metadata) { const uploadTask = storageRef.child(`student_images/${fileName}`).put(file, metadata); return new Promise((resolve, reject) => { // 监听上传状态变化 uploadTask.on('state_changed', (snapshot) => { const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; console.log(`Upload is ${progress}% done`); switch (snapshot.state) { case firebase.storage.TaskState.PAUSED: console.log('Upload is paused'); break; case firebase.storage.TaskState.RUNNING: console.log('Upload is running'); break; } }, // 上传失败时处理错误 (error) => { console.error('Upload failed:', error); reject(error); }, // 上传完成后获取下载链接并resolve async () => { const downloadURL = await uploadTask.snapshot.ref.getDownloadURL(); console.log('File available at', downloadURL); resolve(downloadURL); } ); }); }
然后修改你的submitImages函数为异步函数,用await等待每个文件上传完成:
async function submitImages() { try { // 用let代替var避免异步回调里的变量污染问题 for (let j = 0; j < files1.length; j++) { const metadata = { contentType: 'image' }; const fname = `${stid} - ${files1[j].name}`; // 等待当前文件上传完成,再进行下一个 await uploadSingleFile(files1[j], fname, metadata); } // 所有上传完成后才弹出提示 alert("Images Uploaded Successfully"); } catch (error) { // 有任何一个上传失败都会进入这里 console.error('Upload failed:', error); alert("Some images failed to upload"); } }
方案二:并行上传(同时处理所有文件)
如果想让所有文件同时上传,提升速度,可以用Promise.all批量处理所有上传任务。这种方式适合文件数量较多、希望更快完成上传的场景:
function submitImages() { // 生成所有上传任务的Promise数组 const uploadPromises = files1.map((file, index) => { const metadata = { contentType: 'image' }; const fname = `${stid} - ${file.name}`; const uploadTask = storageRef.child(`student_images/${fname}`).put(file, metadata); return new Promise((resolve, reject) => { uploadTask.on('state_changed', (snapshot) => { const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; console.log(`Upload ${index+1} is ${progress}% done`); switch (snapshot.state) { case firebase.storage.TaskState.PAUSED: console.log(`Upload ${index+1} is paused`); break; case firebase.storage.TaskState.RUNNING: console.log(`Upload ${index+1} is running`); break; } }, (error) => { console.error(`Upload ${index+1} failed:`, error); reject(error); }, async () => { const downloadURL = await uploadTask.snapshot.ref.getDownloadURL(); console.log(`File ${index+1} available at`, downloadURL); resolve(downloadURL); } ); }); }); // 等待所有上传任务完成 Promise.all(uploadPromises) .then(() => { alert("Images Uploaded Successfully"); }) .catch((error) => { console.error('Upload process failed:', error); alert("Some images failed to upload"); }); }
额外注意点
- 原代码里用了
var j,在异步回调中会因为变量提升导致所有回调都引用最后一次的j值,改成let j或者用map的index就能解决这个问题。 - 如果想允许部分文件上传失败、只在全部完成后统计结果,可以用
Promise.allSettled代替Promise.all,然后在结果里过滤成功和失败的任务。
内容的提问来源于stack exchange,提问作者Lucky
相关产品推荐
相关产品推荐

