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

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);
});

Related

how to check conditions inside .then in javascript

Existing code(Working fine) :
.then((result) => {
this.triggerAction(result.id);
}).catch((error) => {
this.errorMsg(error);
});
when i try to add condition inside the .then throws error.
.then((result && result.id) => {
this.triggerAction(result.id);
}).catch((error) => {
this.errorMsg(error);
});
I need to check both result and result.id is coming in the response and throws error id if not available in the result .
To throw an error if result.id is missing, you should do the following:
.then((result) => {
if(!result.id) {
throw new Error("result.id is missing!");
}
this.triggerAction(result.id);
}).catch((error) => {
this.errorMsg(error);
});
You can also do throw result if you just want to pass result to the .catch() block
.then((result) => {
result?.id && this.triggerAction(result.id);
})
.catch((error) => {
this.errorMsg(error);
});
result inside .then is the response you receive on success. You can't apply condition on the fly inside function parameters. You have to check inside the callback function. If results will have id (result?.id) then this.triggerAction(result.id) will invoke
I hope it will work.
Thanks :)

Promise resolving too early

I'm having an issue where my Promise.all is resolving too early. For a test I want to console.log the length of the array which is getting pushed from within the promise map but it is returning 0 sadly. I'm sure it's something simple...
fromPath(job.data.path, { density: 100, format: "png" }).bulk(-1, true).then(output => {
Promise.all(output.map(page =>
jimp.read(Buffer.from(page.base64, 'base64')).then(img =>
{
img.invert().getBase64Async(jimp.AUTO).then(data => imageArray.push(data.replace('data:image/png;base64,', ''))).catch(err => console.log(err))
}
).catch(err => console.log(err))
)).catch(err => console.log(err))
}
// This returns early
).then(console.log(imageArray.length)).then(done()).catch(err => console.log(err));
Any help would be greatly appreciated.
There are a lot of issues there. Mainly they fall into these categories:
Not returning the results of promise chains from fulfillment handlers, which means the chain the fulfillment handler is in won't be linked up to the promise chain you created within it
Calling functions and passing their return value into then, rather than passing a function into then
See inline comments in this minimal, least-changes reworking of that code:
fromPath(job.data.path, { density: 100, format: "png" }).bulk(-1, true)
.then(output => {
// Return the result of `Promise.all`
return Promise.all(
output.map(
page => jimp.read(Buffer.from(page.base64, 'base64'))
.then(img => {
// Return the ersult of this promise chain
return img.invert().getBase64Async(jimp.AUTO)
.then(data => imageArray.push(data.replace('data:image/png;base64,', '')))
// This error handler does nothing useful, since we now return
// the promise chain
// .catch(err => console.log(err))
})
// Recommend **not** handling errors at this level, but using
// `Promise.allSettled` instead
.catch(err => console.log(err))
)
)
// This will only be reached if a *synchronous* error occurs calling (for instance)
// `getBase64Async`, since you've already caught errors above
.catch(err => console.log(err))
})
// Use a function here, don't call `console.log` directly
.then(() => console.log(imageArray.length))
// ^^^^^^
// Pass `done` directly to `then`, don't call it and pass its return value
// (e.g., remove the `()`)
.then(done) // Or `.then(() => done)` if you want `done` called with no arguments
.catch(err => console.log(err));
FWIW, though, if I had to not use async functions I'd probably do it like this:
fromPath(job.data.path, { density: 100, format: "png" }).bulk(-1, true)
.then(output => Promise.allSettled(
output.map(
page => jimp.read(Buffer.from(page.base64, 'base64'))
.then(img => img.invert().getBase64Async(jimp.AUTO))
.then(data => {
imageArray.push(data.replace('data:image/png;base64,', ''));
})
)
))
.then(results => {
// Show errors
const errors = results.filter(({status}) => status === "rejected");
for (const {reason} of errors) {
console.error(reason);
}
// Done
done();
});

Google Cloud Function async with multiple fetch requests

I'm new to both GCF and Javascript async and have been struggling with this. I perform a fetch call initially and then pass that response as a parameter to a second function which then also performs a separate fetch call.
During the second function, my empty initialized json gets properties added to it, and when that function completes, I want to notify the exports.helloHttp to then do res.end and terminate.
I've tried chaining an additional empty then() but it doesn't seem to be working.
My code:
var json = {}; // <- gets properties added to it during secondFunction()
exports.helloHttp = (req, res) => {
fetch("firstfetchurl.com",requestOptions)
.then(result => result.json())
.then(response => {
// next take the result and create a new product
return secondFunction(response);
})
.catch(error => console.log('error', error));
// res.end(JSON.stringify(json)); <- this is what I want my cloud function to output, but only after secondFunction completes
};
Here is the code that would do what you want (replace the fetch URLs and set the appropriate options)
const fetch = require('node-fetch');
exports.helloHttp = async (req, res) => {
return fetch("https://jsonplaceholder.typicode.com/users/1/albums") // First fetch
.then(firstFetchResponse => firstFetchResponse.json())
.then(firstFetchResponse => secondFunction(firstFetchResponse)) // Second fetch
.then(secondFunctionResponse => secondFunctionResponse.json())
.then(finalResponse => res.json(finalResponse)) // This line sends your response to the client
.catch(error => { console.error('Error', error); res.status(500).send('Server Error') }); // In case an error, log and send an error response
};
async function secondFunction(data) {
// Logic of your second function. Here just does another fetch using the data from the first request
let firstAlbumId = data[0].id
return fetch(`https://jsonplaceholder.typicode.com/albums/${firstAlbumId}/photos`);
}
The same function can use an await like this
exports.helloHttp = async (req, res) => {
try {
let response = await fetch("https://jsonplaceholder.typicode.com/users/1/albums") // Note the await on this line
.then(result => result.json())
.then(firstFetchResponse => secondFunction(firstFetchResponse))
.then(secondFetchResponse => secondFetchResponse.json());
res.json(response); // Finally you are sending the response here.
} catch (error) {
console.error(error);
res.status(500).send('Server Error');
}
};
Finally you would also need to make sure that the package.json has the dependency for node-fetch
{
"name": "sample-http",
"version": "0.0.1",
"dependencies": {
"node-fetch": "^2.6.0" // This line must be there
}
}
For sending the JSON response, it uses this method.
result.json() is not an asynchronous operation, therefore you don't need to use a then() block. The following should do the trick;
exports.helloHttp = (req, res) => {
fetch("firstfetchurl.com",requestOptions)
.then(result => {
return secondFunction(result.json());
})
.catch(error => console.log('error', error));
//...
Note that, depending on the exact goal of you helloHttp function, you may need to return the entire promises chain, as follows:
exports.helloHttp = (req, res) => {
return fetch("firstfetchurl.com",requestOptions) // Note the return
.then(result => {
return secondFunction(result.json());
})
.catch(error => console.log('error', error));
//...

javascript promise handling, fail to handle error

I'm having some trouble understanding what I'm doing wrong. I have a function that receives a url to which should make a GET request, in case of success should fill a combo with the received data (this depends which function calls it), in case of fail it should execute some common code.
getFirstCombo = () => {
this.getFromApi('/First/GetAll')
.then(data => this.setState({firstComboOptions: this.parseCombo(data)}))
.catch(error => console.log('ERROR2: ', error));
}
getSecondCombo = () => {
this.getFromApi('/Second/GetAll')
.then(data => this.setState({secondComboOptions: this.parseCombo(data)}))
.catch(error => console.log('ERROR2: ', error));
}
parseCombo = (data: any) => {
const combo = data.map(item => (
{ label: item.description, value: item.id }
));
return combo;
}
getFromApi = (url: string) : Promise<any> => {
return restApiAxios.get(url)
.then(response => {
return response.data;
})
.catch(error => {
console.log('ERROR: ', error);
});
}
this code is executed on the componentDidMount of the react component, but when it fails, it first prints :
ERROR: Error: Network Error
at createError (createError.js:16)
at XMLHttpRequest.handleError (xhr.js:83)
and immediately after:
PanelDatos.tsx:50 ERROR2: TypeError: Cannot read property 'map' of undefined
at PanelDatos.parseCombo (PanelDatos.tsx:55)
at PanelDatos.tsx:50
so, when failing executes the catch block from getFromApi and then it tries to execute the then block in getFirstCombo, which triggers the catch block from the same function cause data does not exist, why is that? shouldnt it just execute the first catch?
thanks in advance
.catch returns a promise much like .then, allowing you to return a custom value and handle it that way.
Try doing the following to observe the effect:
Promise
.reject(1)
.catch(e => e) // Catch the error and return it
.then(console.log) // will log 1 to the console
This means you'll need to add some checks if you want to continue to use promises like this:
Promise
.reject(new Error('haha'))
.catch(err => ({err}))
.then(({err, data}) => {
if(err) return // Do nothing
// enter code here
})
However, using async / await will improve readability even more:
getFirstCombo = async () => {
let response
try {
response = await this.getFromApi('/First/GetAll')
} catch (e) {
return // Exit early
}
let parsed
try {
parsed = this.parseCombo(data)
} catch (e) {
console.log(e)
return // Exit early
}
return this.setState({firstComboOptions: parsed})
}
And, of course, throw the error again in your catch block in your api to allow it to handle api calls.
This is happening since inside getFromApi catch method on the error you are not returning anything, so by default, it is returning a resolved promise with null response and the execution goes inside getFirstCombo then method, causing another error. You can update your code to resolve this like:
getFromApi = (url: string): Promise<any> => {
return restApiAxios.get(url)
.then(response => response.data)
.catch(error => Promise.reject(error));
}
The static Promise.reject function returns a Promise that is rejected. So, it will go directly into catch of wherever getFromApi is called.
DEMO:
async function getFromApi(url) {
return fetch(url) // rejects
.then(response => response.json())
.catch(err => Promise.reject(err))
}
async function getFirstCombo() {
getFromApi('https://no-such-server.abcd')
.then(data => console.log('data: ', data))
.catch(error => console.log('ERROR2: ', error));
}
getFirstCombo()
DEMO #2 (With getFirstCombo function not having any catch block) :
async function getFromApi(url) {
return fetch(url) // rejects
.then(response => response.json())
.catch(err => {
console.log('ERROR in getFromApi(): ', err);
return null; // return null, empty array, 0 or false... as per your requirement
})
}
async function getFirstCombo() {
getFromApi('https://no-such-server.abcd')
.then(data => console.log('data: ', data))
// Same value set in catch block of getFromApi will return in this then() block
// Validate this `data` variable before processing it further like:
// if(data === null) this means an error had occurred
// else continue with your logic
}
getFirstCombo()

How to identify when fetch is completed in vue.js

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
};
})
}

Categories

Resources