I have two functions, login (in fileB.js):
export const login = async (data) => {
try {
const response = await auth.login(data);
return response;
} catch (e) {
return new Error(e);
}
};
and loginProcess (in fileA.js):
const loginProcess = (data) => {
login(data)
.then((response) => {
if (response.status === 200) {
}
})
.catch((e) => {
setError(true);
});
};
If I have an error inside login() function it returns new Error(e) but inside loginProcess() the error from login() is not caught by catch but with then. I need to catch the new Error from login() inside catch in loginProcess(), how can I fix it?
You are converting promise rejection into promise fulfilment by returning an error object.
Retuning a non-promise value from the catch block will fulfil the promise returned by the login function with the return value of the catch block.
To reject the promise returned by the login function:
Re-throw the error caught by the catch block, or
Remove the try-catch block from the login function and let the calling code handle the error.
login function could be re-written as:
export const login = (data) => {
return auth.login(data);
};
I suggest that you choose the second option and re-write the login function as shown above. There is no need for a catch block that just re-throws the error.
Related
I have an internal API that I would like to post data. Depends on some cases, I am seeing errors. So what I would like to do is to call it again if there is an error occurred.
What I did was to create a counter to pass it to the function and call the function recursively as below. This gives me the error as below:
UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
Here is how I call the function:
....
private RETRY_API = 1;
....
try {
await this.callAPI(request, this.RETRY_API);
} catch (error) {
console.log('error', error);
}
This program never comes to the catch block above.
And here is my actual function that I call the API:
private async callAPI(request, retry) {
return new Promise((resolve, reject) => {
someService.postApiRequest('api/url', request, async(err: any, httpCode: number, data) => {
if (this.RETRY_API == 2) {
return reject(err);
} else if (err) {
this.callAPI(request, retry);
this.RETRY_API++;
} else if ( httpCode !== 200 ) {
this.RETRY_API = 2;
// some stuff
} else {
this.RETRY_API = 2;
// some stuff
return resolve(data);
}
});
})
}
Not sure what I am missing. If there is a better way to call the API twice if an error occurred, that would be great if you let me know.
Let's organize a little differently. First, a promise-wrapper for the api...
private async callAPI(request) {
return new Promise((resolve, reject) => {
someService.postApiRequest('api/url', request,(err: any, httpCode: number, data) => {
err ? reject(err) : resolve(data);
});
});
}
A utility function to use setTimeout with a promise...
async function delay(t) {
return new Promise(resolve => setTimeout(resolve, t));
}
Now, a function that calls and retries with delay...
private async callAPIWithRetry(request, retryCount=2, retryDelay=2000) {
try {
return await callAPI(request);
} catch (error) {
if (retryCount <= 0) throw err;
await delay(retryDelay);
return callAPIWithRetry(request, retryCount-1, retryDelay);
}
}
If you can't force a failure on the api to test the error path some other way, you can at least try this...
private async callAPIWithRetry(request, retryCount=2, retryDelay=2000) {
try {
// I hate to do this, but the only way I can test the error path is to change the code here to throw an error
// return await callAPI(request);
await delay(500);
throw("mock error");
} catch (error) {
if (retryCount <= 0) throw err;
await delay(retryDelay);
return callAPIWithRetry(request, retryCount-1, retryDelay);
}
}
It looks like you need to add return await to the beginning of the line this.callAPI(request, retry); in callAPI function.
Similarly there are some condition blocks that doesn't resolve or reject the promise. While it might work okay, it's considered bad practice. You want to either resolve or reject a promise.
I've accomplished calling an API a second time when I received an error by using axios' interceptors functions.
Here is a code snippet you can review:
axios.interceptors.response.use(
// function called on a successful response 2xx
function (response) {
return response;
},
// function called on an error response ( not 2xx )
async function (error) {
const request = error.config as AxiosRequestConfig;
// request is original API call
// change something about the call and try again
// request.headers['Authorization'] = `Bearer DIFFERENT_TOKEN`;
// return axios(request)
// or Call a different API
// const new_data = await axios.get(...).then(...)
// return new_data
// all else fails return the original error
return Promise.reject(error)
}
);
Try replacing
if (this.RETRY_API == 2)
with
if (this.RETRY_API > 1)
I am writing a reusable fetch function (that's going to live in a separate file) and I am not too sure about the best approach to handle a function returning a null.
Say I am want to call that function and apply some logic when getOrganizationInfo doesn't return organization info. At the moment my function returns an error but falls into the first if (organization) block and I can't really handle that. I then need to use that error message to use it in the handleSubmit e.g. showError(error). What shall I do to take advantage of this else logic?
const orgName = 'ABC';
const handleSubmit = async () => {
const organization = await getOrganizationInfo(orgName);
if (organization) {
// Do something
} else {
// Do something else
}
}
Here's my function
export const getOrganizationInfo = async (
organizationName: string,
): Promise<OrganizationInfoResponse> => {
let organizationInfoResponse: OrganizationInfoResponse;
try {
const rawRes = await fetch(`/sometestendpoint/${organizationName}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
organizationInfoResponse = await rawRes.json();
if (rawRes.status >= 400) {
if (organizationInfoResponse.errorCode === ErrorCodes.INVALID_ORG_NAME) {
throw new Error('Given Organization Name is invalid');
} else {
throw new Error('Unable to get organization information.');
}
}
} catch (error) {
// organizationInfoResponse = error.toString();
throw new Error(error.toString());
}
return organizationInfoResponse;
};
The code as provided in your question will do the following when it gets into the catch block:
It executes throw new Error(error.toString())
The promise in the following expression will reject: await getOrganizationInfo(orgName)
The promise that was returned by handleSubmit will reject
None of the code in handleSubmit that follows below this await will execute
So what you claim to happen is not happening. Maybe you were talking about the version of your code where the catch block did not have that throw, but had the commented line instead:
catch (error) {
organizationInfoResponse = error.toString();
}
In that case the code will "fall into the if", because then the error is swallowed by the above catch block:
The function continues with return organizationInfoResponse
The promise in the following expression will fulfill: await getOrganizationInfo(orgName)
The function execution context of handleSubmit is restored and organization is assigned the fulfilment value (i.e. error.toString())
The if (organization) condition is truthy, and so the if block executes
Solution
To get the else block executed, use the throw version of your code, and either introduce a try...catch block in handleSubmit, or (simpler) chain a .then and .catch call on the promise:
const handleSubmit = () => {
return getOrganizationInfo(orgName).then(organisation => {
if (!organisation) throw new Error("Organisation is falsy");
// Do something
}).catch(error => {
// Do something else
});
}
I'm trying to handle a function if it throws an error: create.js
function Apple() {
createDB() //function that saves into db
}
createDB.js
function createDB() {
const Info = new collection(data)
Info.save()
}
Suppose createDB function throws an error when required field in the db is not present. I want to handle such errors.
I tried:
function Apple() {
try{
createDB()//function that saves into db //if throws error go to catch
block
} catch{
function that handles error
}
}
and I also tried:
function createDB() {
return new Promise((resolve, reject) => {
if some condition met{
const Info = new collection(data)
Info.save()
}else{
reject(error)
}
})
}
But it still doesn't goes to the catch block. I'm relatively new to the topic so any suggestions will be really helpful. Basically I want to handle the errors if a function throws error, and it should go to catch block.
You are actually not following the correct syntax. Check out the sampe one:
try {
nonExistentFunction();
} catch (error) {
console.error(error);
// expected output: ReferenceError: nonExistentFunction is not defined
// Note - error messages will vary depending on browser
}
Your updated code with try-catch should follow the above syntax:
function Apple() {
try{
createDB()//function that saves into db //if throws error go to catch
block
} catch (error) {
function that handles error
// here you should log errors or use the logging lib
}
}
Also, if you are using promises, you can follow this approach:
function createDB() {
return new Promise((resolve, reject) => {
if (condition) {
const Info = new collection(data);
Info.save().then(data =>{ resolve(data)})
.catch(e => {console.error(e)}) // handle this promise also
}
else {
reject(error);
}
})
}
Also, you need to understand when to use try-catch block and when to use promises. The try, catch blocks are used to handle exceptions (a type of an error) when the code is synchronous. You should use Promises only for asynchronous functions and nothing else.
Use this sample piece of code
Within try block we write our code which we want to execute
If any error occur controller goes to catch block
In catch block we also receive error
try {
//Here write your code which you want to execute
return true
} catch (error) {
//if there is an any error controller will come into this block and show error
console.error(error);
return false
}
I am new to VueJS but stuck trying to handle a promise after interceptor is triggered.
I have a component called orders (orders/index.vue) and in this component I have a created function as such:
created() {
this.$store.dispatch('user/GETORDERS')
.then(response => {
if (response.status === 200) {
this.data = response.data.data
}
})
}
This dispatch call is mapped to an axios function which calls our API to get the data:
export async function getorders() {
return apiClient
.get('/get-orders')
.then(response => {
return response
})
.catch(err => {
return err.response
})
}
I have an interceptor as such:
apiClient.interceptors.response.use(
(response) => response,
(error) => {
if (error.config.url !== '/login' && error.config.url !== '/logout' && error.response.status === 401) {
store.dispatch('user/HANDLE_401')
return Promise.reject(error)
} else {
throw error
}
})
What I noticed is that when the 401 is triggered (a user is on the app, session expires but they try to navigate to another part of the dashboard, they are logged out as expected (HANDLE_401)). Still, it appears that the created function inside the index.vue component is being triggered after the Promise.reject occurs. I added a console.log() above the if statement in the .then clause to test it. Am I handling the rejection properly? Shouldn't the promise be canceled and no further evaluation occur? Why does the .then trigger if not?
The second then triggers because you are catch-ing the error from the first then. So the first Promise is rejected, you catch this rejection and return err.response - so the second Promise continues normally. If you want your first rejection to propagate through the Promise's chain - you should only add catch on your top-level Promise. Or you should throw an error inside your lower-level catch block(s).
I have a simple async function. It just sends a request and returns the data:
export const updatePanorama = async ({ commit }, payload) => {
const urlEnd = '/v1/pano/update'
const type = 'post'
const resp = await api.asyncRequest(urlEnd, type, payload)
commit('SET_PANORAMA', resp.data)
return resp
}
And this is how I'm using the function:
handleUpdatePanorama (panorama) {
const payload = {}
this.updatePanorama(payload).then(resp => {
this.setIsLoading(false)
this.handleAlert('updateSuccess', 'success')
}).catch(() => {
this.setIsLoading(false)
this.handleAlert('updateError', 'danger')
})
},
The problem is, the code after catch runs if there's an error inside then. But this way I don't know whether the catch error is an request error or and error triggered by the code inside then.
I'm trying try and catch to solve that problem:
handleUpdatePanorama (panorama) {
try {
const payload = {}
const resp = await this.updatePanorama(payload)
console.log('resp:', resp)
this.setIsLoading(false)
this.handleAlert('updateSuccess', 'success')
} catch (err) {
this.setIsLoading(false)
this.handleAlert('updateError', 'danger')
})
},
However, I get an unexpected token error in this line: await this.updatePanorama(payload)
What am I doing wrong?
The problem is, the code after catch runs if there's an error inside then
The solution for that is to not use catch, but the second then parameter. Have a look at the difference between .then(…).catch(…) and .then(…, …) for details.
I'm trying try and catch to solve that problem
That won't work, the catch clause will still be called if there's an exception thrown by setIsLoading or handleAlert.
I get an unexpected token error. What am I doing wrong?
You have not declared the handleUpdatePanorama method as async.
To mitigate the issues and fix the syntax, you could write
async handleUpdatePanorama (panorama) {
var result
try {
const payload = {}
const resp = await this.updatePanorama(payload)
console.log('resp:', resp)
result = ['updateSuccess', 'success']
} catch (err) {
result = ['updateError', 'danger']
} finally {
this.setIsLoading(false)
}
this.handleAlert(...result)
},
If you need to handle errors specifically from updatePanorama, use the second argument to .then(onSuccess, onError)
handleUpdatePanorama(panorama) {
const payload = {}
this.updatePanorama(payload).then(resp => {
this.setIsLoading(false)
this.handleAlert('updateSuccess', 'success')
}, err => {
// handle error from updatePanorama
// you can throw err if you also want to handle the error in .catch()
}).catch(() => {
this.setIsLoading(false)
this.handleAlert('updateError', 'danger')
})
}
note: if you return (or have no return statement) from the error handler, any subsequent .then(onSuccess will execute, if you throw an error (or return Promise.reject() for example, then the .catch() code will also run