How to handle error without crashing function with axios? - javascript

So the problem is, that in some function i want to call getRecord function. It's making request with axios. Some times request can be faild, so i want to handle the error without crashing my function, where i'm calling getRecord.
I'm calling getRecord like this:
const res = await getRecord(eventData)
console.log('handleReadyToRemoveCalls -> res', res)
Here is the getRecord function
const getRecord = ({ extTrackingId, targetId }) => {
console.log('getRecord -> executed')
const apiConfig = require('../config')
return new Promise((resolve, reject) => {
axios({
method: 'get',
url: `https://cloudpbx.beeline.ru/apis/portal/v2/records/${extTrackingId}/${targetId}/download`,
responseType: 'stream',
headers: {
'X-MPBX-API-AUTH-TOKEN': `${apiConfig.token}`,
},
})
.then((response) => {
console.log('getRecordsReference -> response', response)
resolve(response)
})
.catch((err) => {
console.log('getRecordsReference -> err', err)
reject(err)
})
})
}
With this approach i'm suddenly for me getting crashes, when request with axios fails. What's i'm doing wrong?

You are rejecting the promise in the catch block:
.catch((err) => {
reject(err)
})
Thus your propagate the error. If you want the function not to fail just return something that is not an error like an empty array. For instance:
.catch((err) => {
resolve([])
})
One other way to handle this is to reject as you do and to catch the error higher with a try catch like this:
try {
const res = await getRecord(eventData)
} catch(err){
// do whatever you want in case of an error
}

Surround with try catch.
try{
const res = await getRecord(eventData)
}catch(err){
//say dispatch a function and say please try again in UI
}
In you promise you are rejecting the with an error. That error needs to be handled in the block you are calling the function.

Related

assist with fetch function in reactjs

Hello: i am trying to do a fetch to an endpoint in a react component. The fetch i have is working (all console logs show it is working). since this a function so i can reuse it - i want to return something from it. i tried returning the response but i dont think i have my return in the right place or something else is wrong. here is the log i am referring to:
console.log(response)
and here is the full code:
const doFetch = (fetchUri) => {
fetch(fetchUri)
.then(async response => {
const data = await response.json();
// check for error response
if (!response.ok) {
// get error message from body or default to response statusText
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
console.log('running fetch')
console.log(response)
console.log('showing data from fetch')
console.log(data)
return(response)
// this.setState({ totalReactPackages: data.total })
})
.catch(error => {
this.setState({ errorMessage: error.toString() });
console.error('There was an error!', error);
});
};
So I am hoping someone can help me do that 'return' properly. Thanks! Gerard
you did returned properly inside fetch but you did not returned fetch itself :)
const doFetch = (fetchUri) => {
return fetch(fetchUri)
.then(async response => {
const data = await response.json();
// check for error response
if (!response.ok) {
// get error message from body or default to response statusText
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
console.log('running fetch')
console.log(response)
console.log('showing data from fetch')
console.log(data)
return (response)
// this.setState({ totalReactPackages: data.total })
})
.catch(error => {
this.setState({errorMessage: error.toString()});
console.error('There was an error!', error);
});
};
doFetch('randomurl').then(parsedAndPreparedResponse => this.setState({ data: parsedAndPreparedResponse }))
As Michal pointed out in his answer, you're only returning within the fetch, not from doFetch itself.
But to extend a little
The async/await within the then() is unnecessary since no asynchronous call is being made within it. The callback in then() will be run when the fetch promise is resolved.
So either get rid of the async/await or change your code to only use async/await.
If you do it could look something like this:
const doFetch = async fetchUri => {
try {
const response = await fetch(fetchUri)
// check for error response
if (!response.ok) {
throw new Error({
status: response.status,
statusText: response.statusText
});
}
// On successful call
const data = response.json();
// Return the data
return data();
} catch (error) {
// Error handling
}
};
You can call the function like this:
// Using the await keyword
const response = await doFetch(/* your fetchUri */);
// Using .then()
// The callback function in then() will run when the promise from
// doFetch() is resolved
doFetch(/* your fetchUri */).then(res => {
// Whatever you want to do with the response
});
Remember that in order you use the await keyword you need to be inside an async function.
const anAsyncFunction = async () => {
const response = await doFetch(/* your fetchUri */);
}

In JS fetch API promise style, how to get the raw body when the json() function failed

I know this can be solved by writing all codes to async-await style, then can simply write let text = await res.text(); then try catch the JSON.parse(text) and then do decision.
But here I just want to know if there is any way we can achieve that in .then/.catch style.
Consider the below code:
async function test() {
try {
let n = await fetch("https://stackoverflow.com")
.then(res => {
return res.json()
})
.then(data => data.results.length)
.catch(e => {
console.error("Catch 2", e)
})
}
catch (e) {
console.error("Catch 3", e)
}
}
if we execute this function in the browser devtools(F12) with await test(), then there will be an error catch by the "Catch 2" clause. But in the error detail we can only see some logs like JSON parse error.
We cannot see the full text of the response body.
Is there any way that can get the text when the JSON parsing failed?
Your best bet is to look at the response in your devtools' network tab. That will show you the full response.
But if you want to do it in code, you can separate reading the response from parsing it by using the text method instead of the json method, then parsing the text yourself.
The parsing error may be down to the fact you aren't checking for HTTP success. As I noted on my old anemic blog here, fetch only rejects its promise on network errors, not HTTP errors (like 404, 500, etc.). To check for HTTP success, look at the ok or status properties.
Here's the minimal-changes version separating reading the response from parsing it, and checking for HTTP success before reading it at all:
async function test() {
try {
let n = await fetch("https://stackoverflow.com")
.then((res) => {
if (!res.ok) { // ***
throw new Error(`HTTP error ${res.status}`); // ***
} // ***
return res.text(); // ***
})
.then((text) => {
// *** you can look at `text` here in a debugger, or
// *** log it, save it, etc., before parsing below
// *** (which might throw an error)
try {
const data = JSON.parse(text); // ***
return data.results.length;
} catch (error) {
console.error("Parsing error", e);
console.error("Text we were parsing:", text);
}
})
.catch((e) => {
console.error("Catch 2", e);
});
// ...do something with `n`...
} catch (e) {
console.error("Catch 3", e);
}
}
But a couple of things there:
I wouldn't mix async/await with explicit promise callbacks like that.
With that and with your original code, errors will result in n receive the value undefined, because the catch handlers (and my new try/catch block in the then handler) don't return anything.
Instead:
async function test() {
try {
const res = await fetch("https://stackoverflow.com");
if (!res.ok) {
throw new Error(`HTTP error ${res.status}`);
}
const text = await res.text();
// *** you can look at `text` here in a debugger, or
// *** log it, save it, etc., before parsing below
// *** (which might throw an error)
try {
const data = JSON.parse(text);
const n = data.results.length;
// ...do something with `n`...
} catch (error) {
console.error("Parsing error", e);
console.error("Text we were parsing:", text);
}
} catch (e) {
console.error("Catch 3", e);
}
}
Or if you want to respond differently to the parsing error, wrap that bit in a try/catch, etc.
You shouldn't confuse the catch which catching errors in the fetch function itself - with the response errors
fetch("/developer.mozilla.org")
.then(res => {
if (!res.ok) {
console.log("there was an error also here") <= this code also runs
console.log("response is", res);
}
return res.json()
})
.then(data => data.results.length)
.catch(e => {
console.error("Catch 2", e);
})
In your case, you tried converting data -> JSON w/o success, it failed and dropped to the "catch" section.
but to inspect the response - you can dump it in the first section above where I added res.ok
I believe you could do something like this when using promise style Javascript:
const fetchDataPromise = () => {
fetch('https://stackoverflow.com').then((res) => {
res.json().then((jsonData) => {
console.log(jsonData)
}).catch((err) => {
console.error(err)
res.text().then((rawData) => {
console.log(rawData)
}).catch((err) => console.error(err))
})
})
}
Also more intuitive approach would be to use async/await (the trade-off is that you will have to do the API call again):
const fetchData = async () => {
try {
const res = await fetch('https://stackoverflow.com')
const jsonData = await res.json()
console.log(jsonData)
} catch (err) {
try {
console.error(err)
const res = await fetch('https://stackoverflow.com')
const rawData = await res.text()
console.log(rawData)
} catch (rawError) {
console.error(rawError)
}
}
}

How to make a Javascript/React/Typescript fetch call asynchronous?

Consider the following Javascript/React code:
// Javascript function that has a fetch call in it.
export const signIn = (email:string, password:string) => {
console.log("FETCHING...");
fetch(`${endPoint}/sign_in`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email,
password
})
})
.then((response) => {
return response.json()
})
.then(({ data }) => {
console.log("FETCHED DATA...")
})
.catch((error) => {
console.error('ERROR: ', error)
})
console.log("DONE FETCHING...");
}
// A functional component that references signIn.
export const SignIn: React.FC<Props> = () => {
// irrelevant code ...
const onSubmit = (e: CustomFormEvent) => {
e.preventDefault()
console.log("SIGNING IN...")
// calls my signIn function from above
// I don't want this to finish until the fetch inside it does.
signIn(email, password, setAuthentication, setCurrentUser)
console.log("SIGNED IN...");
}
return <>A form here submits and calls onSubmit</>
}
This produces the following console log output:
SIGNING IN...
FETCHING...
DONE FETCHING...
SIGNED IN...
FETCHED DATA...
I want FETCHED DATA... to show up before DONE FETCHING.... I've tried playing around with aysnc/await but that's not working so I don't know where to go from here.
Just add another .then
.then((response) => {
return response.json()
})
.then(({ data }) => {
console.log("FETCHED DATA...")
return
}).then(()=> {
console.log("DONE FETCHING...");
})
.catch((error) => {
console.error('ERROR: ', error)
})
It would have to be in the then statements if you want the console.log to wait until the promise is resolved. Here's an example that uses async/await:
export const signIn = async (email:string, password:string) => {
console.log("FETCHING...");
const response = await fetch(`${endPoint}/sign_in`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email,
password
})
})
const data = await response.json();
console.log("FETCHED DATA...")
console.log("DONE FETCHING...");
}
You would also need to turn this into an async function if you want the console.log to happen after the data is done fetching:
const onSubmit = async (e: CustomFormEvent) => {
e.preventDefault()
console.log("SIGNING IN...")
// calls my signIn function from above
// I don't want this to finish until the fetch inside it does.
await signIn(email, password, setAuthentication, setCurrentUser)
console.log("SIGNED IN...");
}
In order to use async await, you need to return a promise from the call. So basically you don't execute the .then and wrap the call in a try catch block.
export const signIn = async (email:string, password:string) => {
console.log("FETCHING...");
return fetch(`${endPoint}/sign_in`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email,
password
})
})
}
and
const onSubmit = async (e: CustomFormEvent) => {
e.preventDefault()
console.log("SIGNING IN...")
// calls my signIn function from above
// I don't want this to finish until the fetch inside it does.
try {
const data = await signIn(email, password, setAuthentication, setCurrentUser)
// Parse data, do something with it.
console.log("SIGNED IN...");
} catch (e) {
// handle exception
}
}
You may want to look more into how promises in JavaScript works.
One problem here is in signIn. What you're doing right now is:
function signIn() {
// 1. log FETCHING
// 2. call asynchronous fetch function
// 3. log DONE FETCHING
}
The key here is that fetch is asynchronous. The program doesn't wait for it to finish before moving on. See the problem? The JavaScript interpreter is going to run step 3 without waiting for step 2 to finish.
There are multiple ways to fix this. First, you can use then. Here's an example:
promise
.then(res => func1(res))
.then(res => func2(res))
.then(res => func3(res))
Here, you're telling JavaScript to:
Run promise, and wait for it to resolve.
Take the result from promise and pass it into func1. Wait for func1 to resolve.
Take the result from func1 and pass it into func2. Wait for func2 to resolve.
etc.
The key difference here is that you are running each then block in order, waiting for each previous promise to be resolved before going to the next one. (Whereas before you didn't wait for the promise to resolve).
Your code with promises would look like:
export const signIn = (email: string, password: string) => {
console.log("FETCHING...")
// Note that we return the promise here. You will need this to get onSubmit working.
return fetch(/* args */)
.then(res => res.json())
.then(({ data }) => console.log("DONE FETCHING"))
.catch(err => /* HANDLE ERROR */)
}
The second way to fix this is to use async and await. async and await is simply syntax sugar over promises. What it does underneath is the exact same, so make sure you understand how promises work first. Here's your code with async and await:
// The async keyword here is important (you need it for await)
export const signIn = async (email: string, password: string) => {
console.log("FETCHING...");
try {
const res = await fetch(/* args */) // WAIT for fetch to finish
const { data } = res.json()
console.log("FETCHED DATA...")
} catch (err) {
/* HANDLE ERROR */
}
console.log("DONE FETCHING...")
}
There's also a second similar problem in onSubmit. The idea is the same; I'll let you figure it out yourself (the important part is that you must return a Promise from signIn).

sync way of using promises in nodejs

loginTest() gives me resolve(value) so it goes to .then , my problem is since promise is async code, console.log(token) gets printed with promise-pending before promise fulfills.I want to display the value only after promise is fulfilled.Can any one help ?
const token = loginTest().then(res => res).catch(err => err);
console.log(token);
Try this:
loginTest().then(res => {
console.log(res.token);
}).catch(err => err);
This presumes that the token is provided as a field of res. I don't know the structure of the response so you need to check that. Token will not be returned directly from loginTest if it is async.
Use ES6's Async / Await.
const token = await loginTest();
But please note that this line of code needs to be wrapped in a async function. Otherwise, await will not work. And note that await cannot be used in global scope.
For example:
async function getToken() {
const token = await loginTest();
// this next line will execute after the result of of loginTest() returns
console.log(token);
// do something with token after this line
const mutateToken = token + '123456';
}
Documentation of Async / Await found here: Async / Await
You could leverage Async / Await functionality:
const token = await loginTest()
console.log(token)
For Example:
async function getToken () {
const token = await loginTest()
console.log(token)
}
getToken()
You could also do the following for a "synchronous" promises way:
loginTest()
.then(res => res) // Retained for example sake of chaining promises
.then(res => console.log(res))
.catch(err => console.log(err))
This is assuming that token is res, adjust accordingly if it is an object and you need a child property :)!
Try this promise :
var loginTest = new Promise(function
(resolve, reject) {
if (isLogin) {
var login = {
username: 'admin'
//something that related with the loginTest..
};
resolve(login);
} else {
var reason = new Error('some errors..');
reject(reason);
}
})
loginTest
.then((fulfilled) => {
console.log(fulfilled);
})
.catch((error) => {
console.log(error.message);
})
so, loginTest will be printed after fulfilled, then catch error if there are some errors.
You can't access token by doing like below as it is an asyn method.
const token = loginTest().then(res => res).catch(err => err);
console.log(token);
Instead use below
loginTest().then(res => {
const token = res.token;
// your further code goes here.
}).catch(err => err);

Return from .catch(error) not returning in Node / Express

I'm writing a REST API and trying to correctly handle any errors.
When the API call succeeds, the the success object is returned to the calling function and the response is send to the client. But if an error occurs, I want to return the error to the calling function so I can send an error message to the client.
router.delete('/project', (req, res) => {
return DeleteProject(userId, projectId)
.then((response) => {
//handle response
});
});
DeleteProject: (userId, projectId) => {
return deleteProject(userId, projectId)
.then((response) => {
return response
})
.catch((error) => {
console.log('Error in DeleteProject:', error) // This happens.
return error; // this doesn't happen.
})
},
function deleteProject(userId, projectId) {
return Project.deleteOne( ... delete the project... )
.then((response) => {
return response
})
.catch((error) => {
return error
})
}
The .catch(error) in the middle function above, DeleteProject(), gets triggered when an error occurs (ie, the console log happens), but the return doesn't make it's way back to the router.
How can I return the error to be handled by the router?
You can simply remove catch methods from the other two functions, and put the catch function in the router itself. Then the error will itself propagate to your router function
router.delete('/project', (req, res) => {
return DeleteProject(userId, projectId)
.then((response) => {
//handle response
}).catch(() => {
// Add catch function here. Any error in "DeleteProject" and "deleteProject" will propagate to here
})
});
DeleteProject: (userId, projectId) => {
return deleteProject(userId, projectId)
.then((response) => {
return response
});
// Remove catch function
},
function deleteProject(userId, projectId) {
return Project.deleteOne( ... delete the project... )
.then((response) => {
return response
});
// Remove catch function
}
To propagate errors through promise chains you need to throw them. In your catch handler, when you return the error rather than throwing it, you'e setting the (successfully) resolved value of the promise to be the error.

Categories