How to identify when fetch is completed in vue.js - javascript

So I am making an api call in my created() hook, but there are a few things in my app I want to trigger AFTER the api call is finished, but I'm not sure how to do that. On average my API call takes about 5 seconds to return a huge chunk of json (terrible i know). In the example below, the logging statement prints well before the api call has finished.
snippet from component:
<script>
created() {
this.myEndpoint = 'testserver.com'
fetch(this.myEndpoint)
.then(response => response.json())
.then(body => {
for(let i=0; i<body.length; i++){
this.job_execs.push({
'version': body[i].version,
'platform': body[i].platform.name,
})
}
})
.then(console.log('print AFTER the api call is completed'))
.catch( err => {
console.log('Error Fetching:', this.myEndpoint, err);
return { 'failure': this.myEndpoint, 'reason': err };
})
},
};
</script>
I have tried moving the console.log statement to the mounted() hook, but this did not work either.
I believe I could achieve what I want using:
$(window).on('load', function(){
console.log('this logs after the entire page loads')
});
But I'm sure there is a more elegant vue.js solution.
How do I identify in vue.js when the api call in my example has completed

Your code is fine in concept.
The problem is
.then(console.log('print AFTER the api call is completed'))
Even though promise.then calls register async handlers, the calls themselves are evaluated synchronously, they are just supposed to take async callback functions as arguments. When you call
.then(console.log('print AFTER the api call is completed'))
console.log('print AFTER the api call is completed') is evaluated synchronously (logging out your message) and its return value (undefined) is then passed to .then as the callback.
Pass in a function here instead and you should see your log come at the appropriate time:
.then(() => console.log('print AFTER the api call is completed'))

You need to pass a function to the then statement. What you have will execute the console.log and pass its result to the then statement (which is undefined/void).
created() {
this.myEndpoint = 'testserver.com'
fetch(this.myEndpoint)
.then(response => response.json())
.then(body => {
for (let i = 0; i < body.length; i++) {
this.job_execs.push({
'version': body[i].version,
'platform': body[i].platform.name
})
}
})
.then(() => console.log('print AFTER the api call is completed'))
.catch(err => {
console.log('Error Fetching:', this.myEndpoint, err);
return {
'failure': this.myEndpoint,
'reason': err
};
})
}

Related

Why can't I use pushed History.state property set in a fetch API function?

I wrote a function call API using fetch() function and then push received json to History.state using history.pushState(json). I need to use the state property afterwards but when I test console.log(history.state) right after that function, it printed null
What I tried:
function1();
function2();
function function1() {
const request = new Request('https://reqres.in/api/users?page=2');
fetch(request)
.then(response => response.json())
.then(
json =>
{
history.pushState(json,'','');
console.log(history.state)
}
);
}
function function2() {
console.log(history.state);
}
I even tried to wait util history.state not null using while loop (because I think it can be the order problem) but it didn't work. I want to print out exactly what I push to history.state before, this is what actually resulted:
null
// [object Object]
{
"page": 2,
"per_page": 6,
"total": 12,
...
This is a demo of the problem on codepen:
https://codepen.io/L-Ph-t-the-scripter/pen/PoaeqzJ
You have the problem because function1 is async. So you should wait for its response and then running function2.
For this you have 2 options. First is to use async/await function like this:
function async runFunctions() {
await function1();
function2();
}
runFunctions();
function async function1() {
const request = new Request('https://reqres.in/api/users?page=2');
await fetch(request)
.then(response => response.json())
.then(
json =>
{
history.pushState(json,'','');
console.log(history.state)
}
);
}
function function2() {
console.log(history.state);
}
And second way is to call function2 after getting function1 response:
function1();
function async function1() {
const request = new Request('https://reqres.in/api/users?page=2');
fetch(request)
.then(response => response.json())
.then(
json =>
{
history.pushState(json,'','');
console.log(history.state);
function2();
}
);
}
function function2() {
console.log(history.state);
}
Read more about async/await here.
Welcome to Stackoverflow!
function1 uses promises, so function2 is called to early.
Try putting function2 right after the history.pushState call.
function1();
function function1() {
const request = new Request('https://reqres.in/api/users?page=2');
fetch(request)
.then(response => response.json())
.then(
json =>
{
history.pushState(json,'','');
function2();
console.log(history.state)
}
);
}
function function2() {
console.log(history.state);
}
Basically, .then waits for it (the request) to finish but continues running other code while waiting, making function2 run before function1 is finished.
I would recommend reading this or this.
Good luck!

how to make second request if .catch on promise is executed?

how can I make a second request if .catch on promise is executed , in order to get the data successfully
because if .catch is executed I did not received the data. and I have to refresh the page again to get the data.
fetch("https://randomuser.me/api")
.then(result => result.json())
.then(data => {
console.log(data)
})
.catch(error => console.log(error))
You just want to retry in the catch? You will need to call the function that makes the request again. This is called recursion. Ie:
function makeRequest() {
fetch("https://randomuser.me/api")
.then((result) => result.json())
.then((data) => {
console.log(data);
})
.catch((error) => {
console.log(error);
return makeRequest(); // Calls itself recursively
});
}
However this introduces the possibility of an infinite loop, so you need some way to break out, maybe like this:
function makeRequest(attempt=0) {
const maxRetries = 3;
if (attempt > maxRetries) {
throw new Error(`Could not fetch user after ${attempt} attempts`);
}
fetch("https://randomuser.me/api")
.then((result) => result.json())
.then((data) => {
console.log(data);
})
.catch((error) => {
console.log(error);
// Call self and increment the attempt number.
// Very important to ensure that the break condition
// can be met or we can end up calling the function
// forever with no way to escape. This is called an
// infinite loop.
return makeRequest(attempt + 1);
});
}
You can also make the logic more complex for retries, like introduce a timeout before the next request if attempt is gt 0, add exponential backoff etc.
Always remember to be careful of infinite loops when using recursive functions.
You can put the fetch and .json() calls into a function, then call that function once (immediately), and call it again inside the .catch if needed:
const getData = () => fetch("https://randomuser.me/api")
.then(result => result.json())
getData()
.then(console.log)
.catch((error) => {
console.log(error);
return getData();
});
.catch((error2) => {
// Could not get response after 2 attempts
console.log(error2);
});

making second api call after getting data from the first api

I have this first API call which gets me some data and once I have got the data from this api , only then I need to make the second api call . It must happen in the series and not parallelly. How can we do this in react ComponentDidMount?
I am listening to firebase .
Let us suppose that the first api gets me a matchId and now we need to use this matchid to make second call just after first api call without any click .
Let us suppose that this is my first firebase call .
const cardsListener =
getDATA()
.onSnapshot( (doc)=>{
console.log("doc is ",doc);
let data = doc.data();
console.log("data",data);
this.setState({
datainstate:data
});
});
Thanks to async/await, you can wait until an action is done.
componentDidMount() {
this.firstApiCall()
}
async firstApiCall() {
await fetch("http://myurl.com", {
method: 'POST',
body: JSON.stringify(data), // data can be `string` or {object}!
headers:{
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then((responseJson) => {
//if it worked
this.secondApiCall()
})
.catch(error => console.error('Error:', error))
}
secondApiCall() {
}
There are many ways you could do this. My preferred way is to use async await. I'll give an example of how you would use this below.
const getData = async() => {
try {
const apiCall1 = await axios.get(SOME_URL);
const apiCall2 = await axios.get(SOME_URL/apiCall1.data.id)
return apiCall2
} catch(e) {
return e
}
}
It's a silly example but you hopefully get the point. I make my first API call and await the response. Then I make my second API call using some data from the first call then return. You can do whatever logic you want in-between that.
You could also use a callback or promise but I think async await is clearer, and generally less code.
Async Await Documentation - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Promises Documentation - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Callback Documentation - https://developer.mozilla.org/en-US/docs/Glossary/Callback_function
You can follow something like this:
componentDidMount() {
fetch("apiUrl").then(res => {
//Do whatever you want to do with the response or get the id
const {id} = res;
fetch(`apiUrl/${id}`).then(response => {
// Do anything with the response of the api call here
}).catch(error => {
//Thing to do on error
});
}).catch(err => {
//Thing to do on error of 1st api call
});
}

Chain React setState callbacks

I need to load three different json files in an ordered sequence and with a fetch (the reason is i'm using nextjs export and i need those files to be read dynamically, so I fetch them when needed and their content can change even after the export)
The first file contains data that is used to create the url for the second file and so on, so each fetch needs an actually updated state to be fetched,
ATM the solution i'm using, since the second and third files are dependent from the first and second respectively, is fetching the first file and setting some state with setState, then in the setState callback fetch the second file and set some other state and so on:
fetch(baseUrl).then(
response => response.json()
).then(
res => {
this.setState({
...
}, () => {
fetch(anotherUrl+dataFromUpdatedState).then(
response => response.json()
).then(
res => {
this.setState({
...
}, () => {
fetch(anotherUrl+dataFromUpdatedState).then(
response => response.json()
).then(
res => {
this.setState({
})
}
)
})
}
).catch(
error => {
//error handling
}
)
})
}
).catch(
error => {
this.setState({ //an error occured, fallback to default
market: defaultMarket,
language: defaultLanguage,
questions: defaultQuestions
})
//this.setLanguage();
}
)
Now: I know that setState must be used carefully as it is async, but as far as I know the callback function is called after state is updated so from that point of view the state should update correctly. Is this solution anti-pattern, bad practice or should be avoided for some reason?
The code actually works, but i'm not sure if this is the way to do it.
You don't need to use the setState callback and read it from the state, since you can just read the data directly from the res object. This way you can make a flat promise chain.
Example
fetch(baseUrl)
.then(response => response.json())
.then(res => {
this.setState({
// ...
});
return fetch(anotherUrl + dataFromRes);
})
.then(response => response.json())
.then(res => {
this.setState({
// ...
});
return fetch(anotherUrl + dataFromRes);
})
.then(response => response.json())
.then(res => {
this.setState({
// ...
});
})
.catch(error => {
this.setState({
market: defaultMarket,
language: defaultLanguage,
questions: defaultQuestions
});
});

Fetch is done after statements in .then

I have component which is fetching data from server, and then passing result to another component.
getData(args) {
return fetch(`${process.env.REACT_APP_API}/offers?title=${args}`)
.then(response => response.json())
.then((services) => { this.setState({ services }); });
}
In child component I want to pass url in callback function and then do something with result (in this case console.log services), but console.log is firing before fetching data is over.
handleSubmit(event) {
event.preventDefault();
if (this.state.validPhrase === false) return;
this.props.updateSearchPhrase(this.state.searchPhrase);
this.props.onSubmit(this.state.searchPhrase)
.then(console.log(this.props.services));
}
How can I fix this?
then(console.log(this.props.services));
You're immediately calling the console here. You need to enclose it in a function:
then(() => console.log(this.props.services));

Categories

Resources