如何正确等待FileReader.onload成功完成文件加载
问题根源
你的Promise写法错了:resolve(audioDict) 放在了Promise构造函数的同步代码段里,FileReader.onload和后续decodeAudioData都是异步逻辑,还没等这俩执行完,Promise就已经被标记为成功,Promise.all当然会直接走后续回调,根本等不到文件读取和解码结束。
除此之外代码还有两个容易出问题的点:
- 没加任何错误处理:不管是FileReader读文件失败,还是音频解码失败,都没有对应的reject逻辑,出错了Promise会一直卡着没有任何提示
- 先调用
readAsArrayBuffer再绑定onload事件,极端情况(比如本地缓存文件读取速度极快)下会直接错过事件回调,导致逻辑不执行
修正代码
核心改动是把resolve挪到所有异步操作全部完成的位置,补上错误捕获,调整事件绑定顺序:
const promises = []; // 提前初始化audioDict的数组属性,避免索引赋值报错 audioDict.filename = []; audioDict.audio = []; audioDict.sr = []; for (let i = 0; i < files.length; i++) { promises.push( new Promise((resolve, reject) => { const fileReader = new FileReader(); const currentFile = files[i].object; audioDict.filename[i] = currentFile.name; // 先绑定事件,再启动文件读取,避免漏触发 fileReader.onload = (e) => { audioCtx.decodeAudioData(e.target.result) .then((decodedData) => { audioDict.audio[i] = {}; for (let c = 0; c < decodedData.numberOfChannels; c++) { audioDict.audio[i][c] = decodedData.getChannelData(c); } audioDict.sr[i] = decodedData.sampleRate; // 所有处理完成后再决议Promise resolve(audioDict); }) .catch(reject); }; // 捕获文件读取错误 fileReader.onerror = reject; fileReader.readAsArrayBuffer(currentFile); }) ); } Promise.all(promises) .then(() => { console.log("done!"); // 此处可以安全访问完整的audioDict数据 }) .catch((err) => { console.error("处理失败:", err); });
优化建议
如果想让逻辑更不容易出错,可以不用提前按索引往全局audioDict塞数据,每个Promise处理完单个文件后直接返回当前文件的文件名、音频数据、采样率,最后在Promise.all的回调里统一组装成最终对象,能避免循环索引引用带来的偶发问题。
内容的提问来源于stack exchange,提问作者S. Yong
相关产品推荐
相关产品推荐

