如何等待所有上传文件的异步读取完成后再执行lastStep()?
问题核心根源
所有之前的实现跑不通,本质都是踩了FileReader异步特性的坑:
FileReader.readAsDataURL()是浏览器提供的非阻塞异步方法,调用后立刻就返回,完全不会等文件从磁盘读取完- 你写的所有同步代码、没有正确封装的回调逻辑,都会在所有文件读取完成之前就执行完,这就是
lastStep()总是提前运行、拿到空数组的根本原因
之前各版本的具体问题
- 最开始的基础写法:循环里挨个触发文件读取动作后,没有等待任何异步回调,同步执行流直接走到
lastStep(),这时候所有onload回调都还没触发,数组自然是空的 - 尝试async/await的写法:
readAsDataURL()本身不返回Promise,await一个返回值为undefined的方法会立刻继续往下走;而且reader.onload = 回调是同步的赋值操作,await它完全起不到等待异步读取的作用 - 自己写Promise的版本:把整个文件循环逻辑塞在Promise的执行器里,循环只是挨个触发了异步读取,在所有
onload回调触发之前,代码就已经执行到resolve(1)了,then块里的lastStep()还是会提前跑
可正常运行的实现
正确的思路是:把单个文件的完整读取流程封装成独立的Promise,确保每个Promise只有在文件读完、拿到结果后才会resolve;再用Promise.all()等所有文件读取的Promise全部跑完,最后再执行lastStep()。
// 封装单文件读取为可等待的Promise private readFileAsDataURL(file: File): Promise<string | ArrayBuffer | null> { return new Promise((resolve, reject) => { const reader = new FileReader(); // 读取成功后返回文件内容 reader.onload = () => resolve(reader.result); // 补全异常回调,避免读取失败时流程永久卡住 reader.onerror = () => reject(reader.error); reader.readAsDataURL(file); }) } // 改造后的文件选择变更事件 async fileChangedEvent(event: any): Promise<void> { // 把类数组的FileList转成可遍历的数组 const selectedFiles = Array.from(event.target.files) as File[]; // 清空之前的旧缓存,避免多次选文件导致数据错乱累积 this.arrayThumbnail = []; try { // 并行触发所有文件的读取,等待全部完成 const results = await Promise.all( selectedFiles.map(file => this.readFileAsDataURL(file)) ); // 统一把读取结果推入数组 this.arrayThumbnail.push(...results); // 所有前置流程走完,再执行后续业务逻辑 this.lastStep(); } catch (err) { console.error('文件读取失败', err); } }
开发注意事项
- 循环里不要用
var声明FileReader实例,否则由于变量提升的闭包问题,所有循环迭代会共享同一个reader实例,导致读取结果错乱 - 如果一次要选几十上百个大图片,并行读取可能占用过高内存,可以把
Promise.all()的并行模式改成串行:在for...of循环里逐个await单文件的读取结果,每读完一个就push进数组,全部读完再执行lastStep()
内容的提问来源于stack exchange,提问作者Jeb50
相关产品推荐
相关产品推荐

