Answering an interview question - 01
Nath Paiva
Posted on September 12, 2023
Hey there, in this post I want to share learns about Promise
and Audio API
.
A few years ago, I did a technical interview where the interviewer asked me to play a song from an array. Well, I miserably failed, and it wasn’t because I didn’t know about Promise
or the Audio API
.
I failed because when I’m being tested by someone, my brain can’t process the challenge as I wish 🫠. This is an issue I have in my life in many situations. So, regarding code, I'm creating posts in order to develop my confidence and share what I love to do. Maybe in the future, I will be more prepared for those “silly” questions (I hate an interview process. Who doesn’t?).
Anyway, let’s start talking about code. Because this is what we love to do, at least I do love hahahahaha. 😂
The challenge
So, basically the challenge is to create a function
that receives an array
of song paths (string
) and plays each song sequentially. The next song must be played only after the previous one has finished. To resolve this challenge, we must know Audio API
, loops
and asynchronous functions (Promise
).
Audio API
First of all, we have to know that the latest browsers don't let us play media automatically after the page renders. We got this exception:
The user must do some interaction with the page before the script starts the song. So, to make this work first we need to make this interaction happen.
In our HTML file, we can just create a button
, and then create the action to start.
<button type="button">Play</button>
On the JS side, we can add a listener for the button element.
(() => {
// first get the button from the DOM
const buttonElement = document.querySelector('button')
// with this element we can add the addEventListener for the `click` type.
buttonElement.addEventListener('click', () => {
console.log('this message will show after click')
})
})()
Inside the button action, we can trigger the function to load each file and then play the song. Therefore, we can start the function creation which will receive the array and play each song. For this stage we want only to log the file name.
function autoPlaylist(audioList) {
for (let index = 0; index < audioList.length; index++) {
const song = audioList[index]
console.log('🚀 song:', song)
}
}
const buttonElement = document.querySelector('button')
buttonElement.addEventListener('click', () => {
const audioList = ['/samples/sample-01.mp3', '/samples/sample-02.mp3', '/samples/sample-03.mp3']
autoPlaylist(audioList)
})
Loop
As we can see on the code above, the loop chosen to log each value is the for
, we could also use while
or any functional array method such as map
, reduce
or filter,
but not forEach
.
First, we didn't choose any functional array method because after playing the song we won't need any new array.
So, why are we not using forEach
? Basically, in the next step we have to play our audio, and we can't play all at the same time. We have to run it asynchronously. The forEach
method has a different behaviour for async, and instead of waiting to finish the async code and run the next item, the forEach
resolves all at once.
If we just use the Audio API
and call the play
method without changing our function to run as async, all songs will play at the same time. Let's take a look at the following code:
function autoPlaylist(audioList) {
for (let index = 0; index < audioList.length; index++) {
const song = audioList[index]
// start a new instance of Audio with the song path as param
const track = new Audio(song)
// play the track
track.play()
}
}
With this code, the result we will have is all songs playing together. Because the loop doesn't know whether the song has finished or not, nor the content which is running inside it. And well, we don't want it, right? So now it's time to use the Promise
, and let our loop know if should go to the next song or not. The question now is:
What the Promise
does for us?
Promise
is an object that gives us methods which we can call whether an async
action finishes successfully or not. We have to use Promise
if we have to execute something that takes a while to complete and after completion we have to run the next action. With the .resolve()
and .reject()
methods our code will understand exactly when the execution has finished. So, let's take a look at how it works with our songs.
Previously our code was playing all songs at the same time, so we need to control it. The first thing to do is declare our autoPlaylist
function as async
. With this declaration, we are able to wait for an async function to finish to call the next action.
Now we will use async
/ await
and new Promise()
// declare it is an async function
async function autoPlaylist(audioList) {
for (let index = 0; index < audioList.length; index++) {
const song = audioList[index]
/**
* create a new Promise instance and wait for the resolution
* the Promise constructor gives an executor function.
* And it receives two functions as params the `resolve` and `reject`.
* Which we will define that the audio finished.
*
* Now inside the executor function we can start the audio
* */
await new Promise((resolve, _reject) => {
/**
* start the new instance of Audio:
* As we are using before this instance give some methods in order to work
* properly with the audio
* */
const track = new Audio(song)
// with the instance we can play the song
track.play()
// and we can add a listener check when its ends
track.addEventListener('ended', () => {
/**
* when the song has finished we can use the `resolve` function
* from the Promise executor to finish the asynchronous code
* and move on to the next action
* */
resolve()
})
})
}
}
That's all
Phew... Ya, it's a lot. But now we have our autoPlaylist
playing each song sequentially and not all at the same time. Of course, we can break down the code above to have better organization, but after all the main concept is that.
To conclude, in this article we saw loop
, Promise
and Audio API
. Sometimes working with Promise
in run time while we have to wait for something else to continue could not be easy. Nevertheless, now we can understand a bit more how it works. Following, I'm adding the list of documentations I used to create it, and also the demo with the example created here.
Link references
- Audio API
- querySelector
- loop
- map
- reduce
- filer
- forEach
- Promise
- async await
- Do not use forEach with async-await
- Demo
Thanks, see you 👋.
Posted on September 12, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 29, 2024