0

I'm working on a function that can read the duration of a audio file input and then write it in the state. The getDuration function works and I can console log the duration of the audio files. However, I have problems to access the duration outside the getDuration function.

I would like to do something like this:

onChangeAudioFile(e) {
  this.setState({
    selectedFileDuration: getDuration(e.target.files[0])
  };

  function getDuration(file) {
    var objectURL = URL.createObjectURL(file);
    var audio = new Audio([objectURL]);
    var duration = null

    audio.onloadedmetadata = function() {
      console.log(audio.duration);
      duration = audio.duration;
    }

    return duration
  }
}

I'm able to console log the duration, but I can not bring the value out of the function and write it in my state.

I am happy for any clarification.

Greetings

Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
dan_boy
  • 1,735
  • 4
  • 19
  • 50
  • Make `getDuration` an `async` function and add `await` before the `getDuration` call when assigning it to `selectedFileDuration`. – Mr. Polywhirl Nov 02 '20 at 15:44
  • What happens when you run this code? It looks fine to me other than the order. Try moving the function declaration outside of and before `onChangeAudioFile()`. – Code-Apprentice Nov 02 '20 at 15:44
  • Does this answer your question? [How to get returned a value by a callback function](https://stackoverflow.com/questions/14182778/how-to-get-returned-a-value-by-a-callback-function) – Code-Apprentice Nov 02 '20 at 15:45

2 Answers2

1

The duration value isn't readily available, you need to create a promise so you can await the value returned by getDuration.

async onChangeAudioFile(e) {
  this.setState({
    selectedFileDuration: await getDuration(e.target.files[0])
  };

  function getDuration(file) {
    return new Promise((res) => {
      var objectURL = URL.createObjectURL(file);
      var audio = new Audio([objectURL]);

      audio.onloadedmetadata = function() {
        console.log(audio.duration);
        res(audio.duration)
      }
    })
  }
}
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
Mahdi Ghajary
  • 2,717
  • 15
  • 20
1

The problem is that return duration is executed before the onloadedmetadata callback has ever been executed. That event is asynchronous. This is explained in more detail in other Q&As such as

Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference

In your case, instead of getting the value "out" of the callback function, call setState, when/where the value is available: in the onloadedmetadata event handler:

onChangeAudioFile(e) {
  var objectURL = URL.createObjectURL(e.target.files[0]);
  var audio = new Audio([objectURL]);

  audio.onloadedmetadata = () => {
    this.setState({
      selectedFileDuration: audio.duration;
    };
  };
}

See also my blog post about callback misconceptions.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143