0

I have two functions: one plays audio, another one async function that should run after. I should run this sequence N times. But the problem is it runs in parallel. When the audio start playing second function also runs. The question is: how to run this functions consequently? The second function should run when the audio is finished.

  const [song] = useState(typeof Audio !== 'undefined' && new Audio())

  function playSong(songNum) {
    song.src = songs[songNum]
    song.play()
  }

  async function finalFunction() {
    for (let i = 0; i < songs.length; i++) {
      playSong(i)
      await secondFunction().then((res) => {
        console.log(res)
      })
    }
  }

I tried to add promise, but it didn't help, it only runs the first function.

  function playSong(songNum) {
    return new Promise(() => {
      song.src = songs[songNum]
      song.play()
    })
  }

  async function finalFunction() {
    for (let i = 0; i < songs.length; i++) {
      await playSong(i).then(() =>
        secondFunction().then((res) => {
          console.log(res)
        }),
      )
    }
  }
JohnPix
  • 1,595
  • 21
  • 44
  • by "consequently" did you mean "consecutively"? or "as a consequence" as in because of some action? – Mark Schultheiss Aug 29 '23 at 20:44
  • FWIW seems like you are asking for "Promise" here ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise ref: https://stackoverflow.com/q/40029867/125981 – Mark Schultheiss Aug 29 '23 at 20:46
  • Where is `song` defined? What is it? You're setting a src, calling play()... More information regarding this `song` object is required. It probably provides a way to listen to the end of the song - and you'll need to use that in order to set things up as you want. – João Paulo Macedo Aug 29 '23 at 20:48
  • 2
    1. Your Promise is never resolved (or rejected). 2. Why are you mixing await and .then()? 3. https://stackoverflow.com/questions/4619917/how-to-detect-an-audio-has-finished-playing-in-a-web-page – DallogFheir Aug 29 '23 at 20:49
  • `song.src ` and `song.play` both refer to an unwritten song here. – Mark Schultheiss Aug 29 '23 at 20:51
  • If you want one function to run after another, then you want the functions to run **synchronously**, which is how JavaScript natively runs all functions. The JS runtime is only synchronous, so there's nothing special you have to do for that to happen. – Scott Marcus Aug 29 '23 at 20:51
  • It sounds like this is less a question about "making functions run consecutively" (which JavaScript does by default, I don't know of a language which doesn't) but rather "detecting when [an `Audio` object](https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement/Audio) has stopped playing". Re-framing the question to address the underlying issue will help your Google searches. – David Aug 29 '23 at 20:56
  • @DallogFheir it's a good question, if I won't add `await` I'm getting `Uncaught (in promise) DOMException: The play() request was interrupted by a new load request.` – JohnPix Aug 29 '23 at 20:57

3 Answers3

1

You need to make your finalFunction a promise because it's asynchronous.

You can extract all your logic all into one function then make your finalFunction a promise, and then run your code how many times you need to.

Jesse
  • 334
  • 15
  • 1
    What does this mean? Async functions return Promises by default. – DallogFheir Aug 29 '23 at 21:07
  • So you create a function which runs both playSong and finalFunction. However instead of just doing this normally you wrap each in a Promise and then outside of the function you run it as many times as you need to – Jesse Aug 29 '23 at 21:08
1

Something like this:

async function finalFunction() {
    for (let i = 0; i < songs.length; i++) {
    
      const songSrc = songs[i];
      const audio = new Audio(songSrc);
      
      audio.play();
      
      await new Promise( resolve => {
            audio.addEventListener("ended", resolve)
      })

      
      await secondFunction()
    }
    
    // all done
}
  
  
João Paulo Macedo
  • 15,297
  • 4
  • 31
  • 41
0

Not a complete solution, but a starting point to get you on the right track. You will need to adjust it a bit to make it work for your specific use case and incorporate additional functions if you want to.

The idea is to to create a playback chain using a promise and the ended event to wait for the sound to play.

The playInSequence function then recursively calls itself, until there are no songs left. You can also wait for another promise before continuing with the playback.

function playSong(songNum){

    return new Promise((resolve) => {

        let song = new Audio();

        song.addEventListener('ended', () => {

            resolve();

        });

        song.src = songs[songNum];
        song.play();

    });

}

function playInSequence(currentIndex){

    return new Promise((resolve) => {

        playSong(currentIndex).then(() => {

            if(currentIndex + 1 < songs.length){

                playInSequence(currentIndex + 1).then(() => {

                    resolve();

                });

            }
            else{

                resolve();

            }

        });

    });

}

playInSequence(0).then(() => {

    console.log('All done.');

});
Ood
  • 1,445
  • 4
  • 23
  • 43