首页 > 解决方案 > 音频对象持续时间不断变化?

问题描述

我正在尝试获取列表中每个对象的持续时间(以秒为单位),但我的结果遇到了一些问题。我认为这是因为我没有完全理解异步行为。

songs[]我从一组对象开始我的函数n。在我的函数结束时,目标是有一个数组songLengths,其中第一个值是 中第一个对象的持续时间songs[],依此类推。

我试图在这个例子之后为我的函数建模:JavaScript 闭包内循环 - 简单实用的例子。但是我得到了每个songLengths[]索引的未定义值。

$("#file").change(function(e) {
  var songs = e.currentTarget.files;
  var length = songs.length;
  var songLengths = [];

  function createfunc(i) {
    return function() {
      console.log("my val = " + i);
    };
  }

  for (var i = 0; i < length; i++) {
    var seconds = 0;
    var filename = songs[i].name;
    var objectURL = URL.createObjectURL(songs[i]);
    var mySound = new Audio([objectURL]);

    mySound.addEventListener(
      "canplaythrough",
      function(index) {
        seconds = index.currentTarget.duration;
      },
      false,
    );
    songLengths[i] = createfunc(i);
  }
});

标签: javascriptaudiometadataduration

解决方案


像这样的东西应该可以在没有混乱的闭包的情况下工作,并且由于promises而增加了干净的异步性。

基本上,我们声明了一个辅助函数 ,computeLength它采用 HTML5File并执行您已经做过的魔法来计算它的长度。(虽然我确实添加了URL.revokeObjectURL调用以避免内存泄漏。)

然而,promise 不只是返回持续时间,而是使用包含原始文件对象和计算的持续时间的对象来解析。

然后在事件处理程序中,我们映射files选定的以从中创建computeLength承诺,并使用Promise.all等待所有承诺,然后记录结果数组[{file, duration}, {file, duration}, ...].

function computeLength(file) {
  return new Promise((resolve) => {
    var objectURL = URL.createObjectURL(file);
    var mySound = new Audio([objectURL]);
    mySound.addEventListener(
      "canplaythrough",
      () => {
        URL.revokeObjectURL(objectURL);
        resolve({
          file,
          duration: mySound.duration
        });
      },
      false,
    );
  });  
}

$("#file").change(function(e) {
  var files = Array.from(e.currentTarget.files);
  Promise.all(files.map(computeLength)).then((songs) => {
    console.log(songs);
  });
});

推荐阅读