async/await未等待异步执行?Firebase上传首次点击需二次触发求解
问题根因
你遇到的时序错位问题,是三个核心实现错误叠加导致的:
forEach循环不会等待内部的异步操作执行完成:forEach是同步执行的遍历方法,触发完每个文件的上传请求后不会暂停等待,直接顺着代码走到底部的长度判断分支,根本没等上传流程跑完。- 没有把内层的异步Promise向外传递:你在代码里嵌套的
.then回调,没有把这些异步操作的状态抛给外部,导致uploadImages函数本身的await完全感知不到上传流程的进度,函数会在所有异步操作还没跑完的时候就同步返回判断结果。 - 用全局数组的长度做判断的逻辑存在状态残留:
paragraphUrl是全局累计的数组,第一次点击触发的上传流程是在判断分支走完之后才异步完成的,等上传跑完数组长度已经匹配,第二次点击时不需要等新的上传完成,就会直接返回成功结果,造成“第一次点报异常、第二次点才成功”的错位。
修复方法
针对上面的问题,按三个步骤重构即可:
- 替换遍历方式:把原来的
forEach遍历换成map遍历,把每个文件的上传流程封装成独立的Promise,收集成统一的任务数组。 - 统一等待所有异步任务完成:用
Promise.all包裹收集到的任务数组,让uploadImages函数真正等到所有文件上传、所有下载链接获取完成后,再执行返回逻辑。 - 消除状态残留:每次上传流程启动前,先清空用来存链接的数组,同时不再靠全局数组的长度做结果判断,直接从
Promise.all的返回值里取最终的有效链接数做对比,从根源上避免时序错位。
重构后的代码实现
/** 重构后的上传函数 */ async function uploadImages() { // 每次执行前清空累计的链接数组,避免残留旧数据 paragraphUrl = []; const storageRef = firebase.storage().ref(); const fileInputs = document.getElementsByName("fupload"); // 收集每个文件对应的完整上传Promise const uploadTaskList = Array.from(fileInputs).map(async (element) => { const file = element.files[0]; // 跳过未选择文件的输入项 if (!file) return null; const imageRef = storageRef.child(`projects/${projectName}/${file.name}`); // 等待当前文件上传完成 const uploadSnapshot = await imageRef.put(file); // 等待获取当前文件的下载链接 const downloadURL = await uploadSnapshot.ref.getDownloadURL(); return downloadURL; }); // 等待所有上传任务全部执行完成 const taskResults = await Promise.all(uploadTaskList); // 过滤掉空的无效结果 const validDownloadUrls = taskResults.filter(Boolean); // 直接对比有效链接数和输入项数做判断 if (validDownloadUrls.length === fileInputs.length) { return validDownloadUrls; } else { return 1; } }
原来的按钮点击处理函数本身的await逻辑没有问题,不需要做核心调整,只要uploadImages能正确同步返回异步任务的执行结果,就不会出现之前的错位问题。如果偏好更直观的写法,也可以把原来的.then链式调用换成顺序的await语句,减少嵌套层级。
内容的提问来源于stack exchange,提问作者Geethmaka Dissanayake
相关产品推荐
相关产品推荐

