Reputation: 137
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???
Upvotes: 1
Views: 125
Reputation: 17132
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.
Upvotes: 1
Reputation: 172
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>
Upvotes: 0