创建多个 HTMLAudioElement DOM 元素时,有什么方法可以不冻结 UI

Is there any way to not freeze up the UI when creating multiple HTMLAudioElement DOM elements

标题的问题很好self-explanatory。我有这个网站,允许用户一次上传多个文件。然后它将遍历并检索每个的持续时间。

getDuration(file) {
    return new Promise(resolve => {
        let reader = new FileReader();
        reader.onload = e => {
            let audio = new Audio();
            audio.onloadeddata = () => {
                this.duration = audio.duration;
                resolve();
            };
            audio.src = e.target.result;
        };
        reader.readAsDataURL(file);
    });
}

但是,至少在 Chrome 中,这会在 UI 进行所有处理时非常糟糕地冻结它。无论如何我可以避免这种情况或以不同的方式解决这个问题吗?

您已经可以从不使用 FileReader 和 data:// URIs

开始

FileReader 会将所有文件从磁盘读取到内存中,有很多文件,这是很多 IO,您的磁盘(HDD 或 SSD)不会喜欢它。

然后从该文件创建 data:// URL 不是免费操作,因此需要一些 CPU 时间。
最后,浏览器必须将该数据 URL 解码回原始二进制文件,然后才能尝试将该数据解码为音频。这里又是一些不必要的步骤,浪费了很多内存。

相反,您可以简单地从您的用户发送的文件中创建一个 blob:// URL,并将您的 <audio> 的 src 设置为此。这将是一个指向磁盘上实际文件的简单指针,因此浏览器将能够流解码它并仅在内存中附加它需要的数据块。

要创建这样的 blob:// URL,您必须使用 URL.createObejctURL() 方法。

请注意,即使对于来自磁盘的文件,它 并不重要,您应该养成良好的习惯,在使用后撤销此类 blob:// URLs它,因为它们会阻止文件被垃圾收集。

function getDuration(file) {
  return new Promise(resolve => {
    const audio = new Audio();
    audio.onloadedmetadata = () => {
      URL.revokeObjectURL(file)
      resolve(audio.duration);
    };
    audio.src = URL.createObjectURL(file);
  });
}
document.querySelector("input").oninput = async (evt) => {
  console.log( await getDuration( evt.target.files[0] ) );
}
<input type="file">