const button = document.getElementById('button');
async function initialRequest() {
const data = await fetch("https://api.stackexchange.com/2.2/questions?page=1&pagesize=5&order=desc&sort=creation&site=stackoverflow")
.then((res) => res.json())
.then((data) => {return data.items});
return data;
}
button.onclick = async (e) => {
let questionCount = document.getElementById('limitVal');
const value = questionCount.value;
const data = initialRequest();
console.log(data);
}
When I try running the above code block, I get a network error from this code and the browser debugger points out the closing bracket in the initialRequest function as an issue. However, when I make the request from inside the event callback, it works fine. The issue is there is a lot more code that will be going into this project and I want to split things up as much as I can.
As mentioned by #Aniket and #Linda Paiste, adding await seems to do the trick for me. See example:
const button = document.getElementById('button');
async function initialRequest() {
const data = await fetch("https://api.stackexchange.com/2.2/questions?page=1&pagesize=5&order=desc&sort=creation&site=stackoverflow")
.then((res) => res.json())
.then((data) => {return data.items});
return data;
}
button.onclick = async (e) => {
// let questionCount = document.getElementById('limitVal');
// const value = questionCount.value;
const data = await initialRequest();
console.log(data);
}
<button id="button">Button</button>
Related
I am trying to load data from firebase by calling a function in which it filters data and returns them.
When I call this function in my main function, it returns "undefined". I know the data is there (console.log(postsArray)) prints the data but I guess the return executes before data is loaded.
What am I doing wrong?
calling_Function_in_Main = async () => {
const data = await FirebaseData ();
console.log(data);
};
FirebaseData is the function that I call in my main function to load data and to return them
let postsArrays=[];
const FirebaseData = async () => {
const getViewableLink = async (link) => { //some function };
const loadData = async () => {
const database = firebase.database();
const data = database.ref();
const loadProfile = data
.child('Posts')
.orderByChild('Active')
.equalTo(true)
.once('value', function gotData(data) {
Object.values(readInfo).forEach(async (element) => {
element.Option1Link = await getViewableLink(
preLink + element.Option1Link,
);
postsArray.push(element);
}
});
})
.catch((error) => {
console.log(error);
}
})
.then((postsArray) => {
console.log(postsArray);
return postsArray;
});
};
await loadData();
};
export default FirebaseSwipeData;
You can't use foreach with async/await because It is not asynchronous. It is blocking.
you have 2 ways to fix this:
1- Reading in sequence: you can use for...of loop
for(const element of Object.values(readInfo)) {
element.Option1Link = await getViewableLink(
preLink + element.Option1Link,
);
postsArray.push(element);
}
2- Reading in parallel: you can use Promise.all
await Promise.all(Object.values(readInfo).map(async (element) => {
element.Option1Link = await getViewableLink(
preLink + element.Option1Link,
);
postsArray.push(element);
}));
Hope that solves the problem, for you
How to create a request queue? if you build sequential queries, then they are called simultaneously
const s1 = async () => {
axios(url_benzinga)
.then(response => {
var html = response.data;
var $ = cheerio.load(html)
const s2 = async () => {
axios(url_benzinga)
.then(response => {
var html = response.data;
var $ = cheerio.load(html)
I can not understand how to do it right
https://ibb.co/ngPr45p
https://github.com/axios/axios/issues/371
It would look something like:
axios.get('http://google.com')
.then((res) => {
// do something with Google res
return axios.get('http://apple.com');
})
.then((res) => {
// do something with Apple res
})
.catch((err) => {
// handle err
});
Alternatively you can send both requests simultaneously and handle responses at the same time:
axios.all([
axios.get('http://google.com'),
axios.get('http://apple.com')
])
.then(axios.spread((googleRes, appleRes) => {
// do something with both responses
});
i think this is not the right solution
axios.get('api')
.then(res => {
// first save this api response anywhere. then after that particular action call next api otherwise i think it will be complex or sometime axios . then return an error so should also aware from such error
})
.catch(err => console.log(err.message))
It is easy with async/await syntax.
Example, you have 2 requests like:
const s1 = async () => {
return axios.get('http://google.com');
}
const s2 = async () => {
return axios.get('http://apple.com');
}
Now, if you want to take request in sequential
// sequential queries,
const res1 = await s1();
const res2 = await s2();
// do something with both responses
and, request in parallel
const [res1, res2] = await Promise.all([
s1(),
s2(),
]);
// do something with both responses
I try to fetch some object, but the problem is that I need to check first if there ist any object on cache endpoint, if not I should do normal fetching from regular endpoint.
So far I only managed to do fetching from:
Normal endpoint and set everything on state,
Cache endpoint and set everything on state
Any attempts to mix both methods ended in failure :(
How can I mix this two methods?
const getCache = async () => {
try {
const apiCall = await fetch(fetchCacheEndpoint)
const data = await apiCall.json()
return data
} catch (e) {
console.log(e);
}
}
const pageOne = getCache().then((result) => {
const convertedOBj = result.doSomeSeriousStuff()
this.setState({
desiredObj: convertedOBj
})
})
I expected to do something like this
const getCache = async () => {
try {
const apiCall = await fetch(fetchCacheEndpoint)
const data = await apiCall.json()
return data
} catch (e) {
console.log(e);
}
}
let convertedOBj
const pageOne = getCache().then((result) => {
if ((result === undefined) || (!result) || (result && !result.length > 0)) {
const makeRegularFetch = async () => {
const regularFetch = await fetch(regularEndPoint)
const data = await regularFetch.json()
}
const pageTwo = makeRegularFetch ().then((result) => {
convertedOBj = result.doSomeSeriousStuff()
this.setState({
desiredObj: convertedOBj
})
})
} else {
convertedOBj = result.doSomeSeriousStuff()
this.setState({
desiredObj: convertedOBj
})
}
})
After first fetch (getCache) is failed there is another fetch (makeRegularFetch) to second endpoint which is always fine, but only in the case when first(getCache) return empty object or just any kind of error
How can I handle this kind of action?
From what I can see in your second part of your code, you never execute your pageOne function.
Try pageOne() after your definition.
However I made a fiddle on stackblitz for your case: https://stackblitz.com/edit/js-eufm8h
If you don't understand something, feel free to ask.
I'm building a simple testing site. How it works: A user accepts a task (assignment), sends an answer, which is then pushed to an array called answers.
What I need to do, is check if the answers array in the assignment object is defined. If it is it means user has submitted at least one answer and he's good to submit.
The problem is the async. I'm not sure how to make the code wait for the query to finish.
Here's my code:
export default class FinishAnswerButton extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleFinishAssignment = this.handleFinishAssignment.bind(this);
}
async handleFinishAssignment() {
var currentAssignment = firebase.database().ref('Works').child(this.props.assignmentId);
console.log('props points', this.props.points);
var outerThis = this;
await currentAssignment.on('value', snapshot => {
console.log(snapshot.val());
});
var user = firebase.database().ref('Users').child(firebase.auth().currentUser.uid);
await user.once('value', snapshot => {
var acc = snapshot.val();
var points = outerThis.props.points + acc.points;
user.child('points').set(points).catch(e => console.log(e));
// user.child('assignedWork').remove().catch(e => console.log(e));
// currentAssignment.child('state').set('Completed').catch(e => console.log(e));
// currentAssignment.child('finishTime').set(Time.generate()).catch(e => console.log('finishTime', e));
});
return <AssignmentsComponent/>
}
render() {
return (
<Button onClick={this.handleFinishAssignment}>Zakoncz rozwiazywanie zadania (Upewnij
sie ze uczen ma wszystko czego potrzebuje!)</Button>
)
}
}
I tried solving it like this:
async handleFinishAssignment() {
var currentAssignment = firebase.database().ref('Works').child(this.props.assignmentId);
console.log('props points', this.props.points);
var outerThis = this;
currentAssignment.once('value', snapshot => {
console.log(snapshot.val());
return snapshot.val();
}).then(assignment => { // here
console.log(assignment);
var user = firebase.database().ref('Users').child(firebase.auth().currentUser.uid);
if (assignment.answers.length > 0) {
console.log('if');
user.once('value', snapshot => {
var acc = snapshot.val();
var points = outerThis.props.points + acc.points;
user.child('points').set(points).catch(e => console.log(e));
// user.child('assignedWork').remove().catch(e => console.log(e));
// currentAssignment.child('state').set('Completed').catch(e => console.log(e));
// currentAssignment.child('finishTime').set(Time.generate()).catch(e => console.log('finishTime', e));
}).catch(e => console.log(e));
} else
console.log('else');
});
return <AssignmentsComponent/>
}
But it turned out, the assignment passed to the callback is a promise, not the actual snapshot.val(), which is basically what I need in the next part of the code.
Also, why does adding await not solve the issue? I thought the whole idea of await is to make the code act synchronously. Or am I using it wrong?
So to recap: I need to make this code wait. I need to use the response from the await currentAssignment.on('value', snapshot => { to be available in the next query, which is: await user.once('value', snapshot => {. How can I achieve this?
Ok I figured it out. I was on the right track, but got a mental hiccup somewhere on the way. Here's the working code:
async handleFinishAssignment() {
var currentAssignment = firebase.database().ref('Works').child(this.props.assignmentId);
var outerThis = this;
currentAssignment.once('value').then(snapshot => { // Right here
var assignment = snapshot.val();
console.log(assignment);
var user = firebase.database().ref('Users').child(firebase.auth().currentUser.uid);
if (assignment.answers !== undefined) {
console.log('if');
user.once('value', snapshot => {
var acc = snapshot.val();
var points = outerThis.props.points + acc.points;
user.child('points').set(points).catch(e => console.log(e));
// user.child('assignedWork').remove().catch(e => console.log(e));
// currentAssignment.child('state').set('Completed').catch(e => console.log(e));
// currentAssignment.child('finishTime').set(Time.generate()).catch(e => console.log('finishTime', e));
}).catch(e => console.log(e));
} else
console.log('else');
});
return <AssignmentsComponent/>
}
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???
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.
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>