1

Hi I'm having an async issue, when I'm trying to filter the audioFile array it's empty I need the fetch to happen before the filter, how do I do this?

const media = 'audio';
const searchInput = document.querySelector('.search');
const output = document.querySelector('.output')

searchInput.addEventListener('change', searchData);

async function searchData() {
  let search = this.value;
  const finalData = await getData(search);
  render(finalData);
}

function render(data) {
    let html;
if(media == 'image') {
   html = data.map(result => {
    return `
        <div class="image">
            <img src="${result.links[0].href}"/>
        </div>
    `
  }).join("");
} else {
          const audioFiles = [];
          data.map(result => {
            fetch(result.href)
            .then(blob => blob.json())
            .then(data => audioFiles.push(...data));
          })
          console.log(audioFiles);
          /* filter .mp3? */
          const regex = new RegExp(".mp3$");
          const files = audioFiles.filter(file => {
            return file.match(regex);
          })

          console.log(files);

}
output.innerHTML = html;

}

function getData(search) {
  const endpoint = `https://images-api.nasa.gov/search?q=${search}&media_type=${media}`;

  return fetch(endpoint)
    .then(blob => blob.json())
    .then(data => data.collection.items);
}
<input type="text" class="search" placeholder="planet">
<div class="output"></div>

audio files are empty before the filter how do I fix it so the audio files are there before I do the filter on it???

2 Answers 2

1

Looks like you can just chain more .then()s onto your existing.

The issue is that:

      data.map(result => {
        fetch(result.href)
        .then(blob => blob.json())
        .then(data => audioFiles.push(...data));
      })

      // This part below is executing before `audiofiles` is populated
      console.log(audioFiles);
      /* filter .mp3? */
      const regex = new RegExp(".mp3$");
      const files = audioFiles.filter(file => {
        return file.match(regex);
      })

      console.log(files);

try:

      data.map(result => {
        fetch(result.href)
        .then(blob => blob.json())
        .then(data => audioFiles.push(...data))
        .then(() => new RegExp(".mp3$"))
        .then((regex) => audioFiles.filter(file => file.match(regex)))
        .then((files) => {
          console.log(files)

          // Set the HTML once it is done getting the audio
          output.innerHTML = html
        })
      })

We are using implicit returns.

This:

runFunction().then(data => console.log(data))

is the same as:

runFunction().then(data => {
  return console.log(data)
})

if a function just returns one expression, even .then(data => data += 5).

You have to think about what JavaScript is doing when you execute your original code. First, it starts fetching the audio files, which returns a Promise, so it instantly gets put into the function queue, and then continues executing the code. It starts by executing console.log(audiofiles) but the fetch in the function queue hasn't pushed into audiofiles yet because 2 ms ago, that Promise chain started executing blob.json(). It then continues down through your code and creates a new RegExp and sets the value of files synchronously because .filter() is synchronous. Then it executes console.log(files) which contains some garbage because audio files may or may not have data pushed into it yet. Reasonably, in most cases, there will be no value yet.

The solution is to force the code to wait, and you are using some async/await syntax already, so you could use it here also...

Here is perhaps a better solution that is also easier to read, extend, and maintain:

} else {
  data.map(result => fetch(result.href))
      .then(async (blob) => {
         const data = await blob.json();

         const audiofiles = [...data];
         console.log(audioFiles);

         /* filter .mp3? */
         const regex = new RegExp(".mp3$");

         const files = audioFiles.filter(file => file.match(regex));
         console.log(files);

         output.innerHTML = html;
       })
}

I interpretted your code as if you wanted to load the html last, but in accordance to proper async behaviour, it would be optimal if the page loads ASAP, and the audio files load in after when they are ready. That may be your original intention, in which case, move it back outside the if/else block.

Sign up to request clarification or add additional context in comments.

4 Comments

thanks @agm1984 , would i be able to send you my code and you could point me in the right direction i think i've nearly done it
Run it and test the new behaviour, see if you can track the value you want at every step of the way through the code. Put lots of console.logs in every line if you need to :)
oh thanks agm , i think i've done it will upvote now (: !
Try and practice async/await more, it is extremely powerful for controlling async flow and makes the code super easy to read and very concise. I noticed in your code, you are not using .catch() blocks also. You should have those, even just .catch((error) => console.log(error)). I will leave it up to you to research those. While you're at it, do some reading about try/catch blocks (for your async/await research). Proper error handling is important.
0

You should wait for resonse from fetch to be able to filter them, I hope render function can be async as well

const media = 'audio';
const searchInput = document.querySelector('.search');
const output = document.querySelector('.output')

searchInput.addEventListener('change', searchData);

async function searchData() {
  let search = this.value;
  const finalData = await getData(search);
  render(finalData);
}

async function render(data) {
    let html;
if(media == 'image') {
   html = data.map(result => {
    return `
        <div class="image">
            <img src="${result.links[0].href}"/>
        </div>
    `
  }).join("");
} else {
          const regex = new RegExp(".mp3$");

          const files = await Promise.all(data.map(async result => {
            const response = await fetch(result.href)
            const blob = await blob.json()
            return blob.filter(file => file.match(regex));
          }))

          console.log(files);

}
output.innerHTML = html;

}

function getData(search) {
  const endpoint = `https://images-api.nasa.gov/search?q=${search}&media_type=${media}`;

  return fetch(endpoint)
    .then(blob => blob.json())
    .then(data => data.collection.items);
}
<input type="text" class="search" placeholder="planet">
<div class="output"></div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.