0

I have some code in which I get the duration of a .wav file and process it. In order to do this, I try to load the metadata of the file.

onload = () => {
    // make an Audio object which represents a wav file
    const queryString = window.location.search;
    const urlParams = new URLSearchParams(queryString);
    let audio = new Audio(urlParams.get('src'));
    audio.volume = urlParams?.get('volume') ?? 1;
    audio.play()
    
    // log the duration of the wav file
    console.log(getDuration(audio))

    // do processing on the duration of the file
    setTimeout(() => {
        close();
    }, urlParams?.get('duration') ?? getDuration(audio));
}

async function getDuration(audio) {
    let audioTag = document.getElementById('get_duration')
    audioTag.src = audio.src

    // attempt to return duration
    audioTag.addEventListener('loadedmetadata', function(){
        return Promise.resolve(audioTag.duration)
    })
}

This fails because there is an implicit return undefined at the end of getDuration function, causing it to return an undefined Promise object before the loadedmetadata event occurs.

I could use a callback function like so:

onload = () => {
    // make an Audio object which represents a wav file

    // log the duration of the wav file
    console.log(getDuration(audio, process))
}

function process(duration) {
    // do processing on the duration of the file
}

function getDuration(audio, func) {
    let audioTag = document.getElementById('get_duration')
    audioTag.src = audio.src

    // duration successfully obtained 
    audioTag.addEventListener('loadedmetadata', function(){
        func(audioTag.duration)
    })
}

but this seems inelegant and unscalable, especially considering that getting the duration of a file should be simple.

Is there any way to return the duration of a wav file (or wait for an event in general) without using a callback function?

Jeffrey H.
  • 119
  • 3
  • you could make `function getDuration` (no need for async) return a Promise, the current code in the first code can be put inside the Promise executor, and resolved inside the *loadedmetadata* handler function - then of course, you have to handle the promise returned by `getDuration` where you call it – Bravo Feb 22 '22 at 04:32
  • Probably relevant: [How do I convert an existing callback API to promises?](https://stackoverflow.com/q/22519784) – VLAZ Feb 22 '22 at 04:32

1 Answers1

1

I found a solution from https://stackoverflow.com/a/70789108/16236499.

onload = () => {
    // make an Audio object which represents a wav file
    
    // log the duration of the wav file
    console.log(getDuration(audio))

    // do processing on the duration of the file
}

// https://stackoverflow.com/a/70789108/16236499
async function getDuration(audio)
{
    let audioTag = document.getElementById('get_duration')
    audioTag.src = audio.src

    await getPromiseFromEvent(audioTag, 'loadedmetadata')
    
    return audioTag.duration
}

function getPromiseFromEvent(item, event) {
    return new Promise((resolve) => {
        const listener = () => {
            item.removeEventListener(event, listener)
            console.log(item.duration)
            resolve()
        }
        item.addEventListener(event, listener)
    })
}
Jeffrey H.
  • 119
  • 3