Oliver Sewell Oliver Sewell - 3 years ago 145
HTML Question

async issue filter won't work because array is empty

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???

Answer Source

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.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download