In axios, why throw new Error() is not allowed inside catch()?
I am in such requirement, where if an error is returned by the server, the catch block should throw an error, which later on will be handled by redux-saga and appropriate action would be dispatched.
The API call:
export const userSignupApi = userDetails => {
const endpoint = `${URL_ROOT}${URN_SIGNUP}`;
axios
.post(endpoint, userDetails)
.then(response => {
console.log("Response: ", response);
//do something with the response
})
.catch(error => {
throw new Error(error.response.data.message);
});
};
I am getting Unhandled Rejection (Error) because of the above catch block.
Below is my saga which handles the operation:
import { call, takeLatest, put } from "redux-saga/effects";
function* handleUserSignup(action) {
try {
yield call(userSignupApi, action.userDetails);
yield put(userSignupSuccess()); //dispatching success action
} catch (error) {
yield put(userSignupFail(error)); //dispatching error action
}
}
function* watchUserSignup() {
yield takeLatest(NEW_USER.SIGNUP, handleUserSignup);
}
Edit: Why I want the above code structure? Because this makes easy to unit test the API code and the saga code.
The Promise created in userSignupAPI isn't being used anywhere (it's not even being returned), so when an error is thrown inside catch, the Promise chain resolves to an (uncaught) rejected Promise, resulting in the error.
In the caller of userSignupAPI, you should await the call, so that the try/catch inside handleUserSignup will see the thrown error:
export const userSignupAPI = userDetails => {
const endpoint = `${URL_ROOT}${URN_SIGNUP}`;
return axios
.post(endpoint, userDetails)
.then(response => {
console.log("Response: ", response);
})
.catch(error => {
throw new Error(error.response.data.message);
});
};
async function* handleUserSignup(action) {
try {
yield await call(userSignupApi, action.userDetails);
yield put(userSignupSuccess()); //dispatching success action
} catch (error) {
yield put(userSignupFail(error)); //dispatching error action
}
}
function* watchUserSignup() {
yield takeLatest(NEW_USER.SIGNUP, handleUserSignup);
}
(make sure that call returns the Promise returned by userSignupApi)
I got this working. I was doing it completely wrong. As suggested by #certianPerformance and after reading some questions and github issues, I got the right way to handle this. Instead of returning the api response, I should have returned the promise.
Here is the solution:
export const userSignupApi = userDetails => {
const endpoint = `${URL_ROOT}${URN_SIGNUP}`;
return axios.post(endpoint, userDetails);
};
Saga:
import { call, takeLatest, put } from "redux-saga/effects";
function* handleUserSignup(action) {
try {
const response = yield call(userSignupApi, action.userDetails);
response.then(response => {
const location = response.headers.location;
put(userSignupSuccess(location));
put(newUserWelcomeNote("Welcome user"));
});
} catch (error) {
const errorMessage = error.response.data.message;
yield put(userSignupFail(errorMessage));
}
}
function* watchUserSignup() {
yield takeLatest(NEW_USER.SIGNUP, handleUserSignup);
}
You are using try catch because you don't want to throw error in browser console you want to handle in catch. Throwing error in catch will remove its purpose.If you want to throw error remove try catch(which is not a recommended way)
UPDATE
For axios catch method catches any error thrown by api url. If you don't want to catch error and show it in browser, you can remove catch block or you can call the action which handles error. For more information about promise catch you can refer here
Related
I have the following code on python using flask
#bp.route("/test", methods=["GET"])
def test():
throw_error = True
if throw_error :
return jsonify(message="Throwing an error"), 405
return jsonify(message="Test message"), 200
on React I have a context setup with the following function
function testRequest(){
const response = axios.get('/api/test')
console.log(response)
}
I'm calling this function on a button click in another component by
async function handleButtonClick(e){
e.preventDefault();
try{
await testRequest();
}catch(error) { // DOESN'T EXECUTE??
console.error("Error occured")
setError("Error occurred in test method")
}
}
Why isn't the try catch, catching the 405 error?
You can only usefully await a promise. testRequest doesn't return a promise.
It triggers axios.get, assigns the promise to response, logs it, then returns undefined.
The try/catch doesn't touch the promise at all.
You could fix that with:
function testRequest(){
const response_promise = axios.get('/api/test')
console.log(response_promise)
return response_promise;
}
So the promise is being awaited inside the try/catch.
With below modification to your function, you will be able to catch error.
async function testRequest() {
const response = await axios.get('http://localhost:1337/sample/test');
return response;
}
async function handleButtonClick(e:any) {
e.preventDefault();
try {
await testRequest();
} catch (error) { // DOESN'T EXECUTE??
console.error("Error occured")
console.log(error.mes);
}
}
I have the following code in my own async function that uses another imported function from module which is a custom wrap of axios inside try/catch block:
async function getCharacter (realmSlug, characterName) {
try {
const [{id, name, gender, faction, race, character_class, active_spec, realm, guild, level, last_login_timestamp, average_item_level, equipped_item_level}, {pets, unlocked_battle_pet_slots},{mounts}] = await Promise.all([
getCharacterSummary(realmSlug, characterName), -- custom axios instance
getCharacterPetsCollection(realmSlug, characterName),
getCharacterMountsCollection(realmSlug, characterName)
])
....
return result;
} catch (error) {
console.log(error.code);
if (error.response.status === 404 || error.response.status === 403) {
console.error(`${getCharacter.name},${characterName}#${realmSlug}`);
}
return { name: characterName, realm: realmSlug }
}
}
The problem is that if I use promise.all according to Stackoverflow 1,2 I can not handle errors. So the problem is when I call function to execute, my errors doesn't handle in (catch) block. At all. Even if I don't need print them, anyway I receive messages in console about 404 errors, but console.log(error.code) still gives me nothing. For example:
So is there any way to handle this annoying error messages in console somehow?
For example using .catch somewhere? Or using for await ... of or rxJS instead if it's possible?
Exporting function and using .catch
Even if I export this function getCharacter in another .js file and use the following code:
const getCharacter = require('./getCharacter');
let bulkCharacters = [{realmSlug, characterName},{realmSlug, characterName},... ,n] //array of characters for getCharacter request
const promises = bulkCharacters.map(async ({realmSlug, characterName}) => {
try {
return await getCharacter(realmSlug, characterName);
} catch (e) {
console.log(e)
}
});
let test = await Promise.all(promises)
.catch(function(arrayOfPromises, err) {
// log that I have an error, return the entire array;
console.log('A promise failed to resolve', err);
return arrayOfPromises;
})
.then(function(arrayOfPromises) {
console.log(arrayOfPromises)
})
;
console.log('stop')
I still receive errors in console, without triggering catch block inside getCharacter function or this file in which this function was imported and catch block is outside the function.
im using nodejs 8. I've replaced promise structure code to use async and await.
I have an issue when I need to return an object but await sentence resolve undefined.
This is my controller method:
request.create = async (id, params) => {
try {
let request = await new Request(Object.assign(params, { property : id })).save()
if ('_id' in request) {
Property.findById(id).then( async (property) => {
property.requests.push(request._id)
await property.save()
let response = {
status: 200,
message: lang.__('general.success.created','Request')
}
return Promise.resolve(response)
})
}
}
catch (err) {
let response = {
status: 400,
message: lang.__('general.error.fatalError')
}
return Promise.reject(response)
}
}
In http request function:
exports.create = async (req, res) => {
try {
let response = await Request.create(req.params.id, req.body)
console.log(response)
res.send(response)
}
catch (err) {
res.status(err.status).send(err)
}
}
I tried returning Promise.resolve(response) and Promise.reject(response) with then and catch in the middleware function and is occurring the same.
What's wrong?
Thanks a lot, cheers
You don't necessarily need to interact with the promises at all inside an async function. Inside an async function, the regular throw syntax is the same as return Promise.reject() because an async function always returns a Promise. Another thing I noticed with your code is that you're rejecting promises inside a HTTP handler, which will definitely lead to unexpected behavior later on. You should instead handle all errors directly in the handler and act on them accordingly, instead of returning/throwing them.
Your code could be rewritten like so:
request.create = async (id, params) => {
let request = await new Request(Object.assign(params, { property : id })).save()
if ('_id' in request) {
let property = await Property.findById(id)
property.requests.push(request._id)
await property.save()
}
}
And your http handler:
exports.create = async (req, res) => {
try {
await Request.create(req.params.id, req.body)
res.send({
status: 200,
message: lang.__('general.success.created','Request')
})
} catch (err) {
switch (err.constructor) {
case DatabaseConnectionError: // Not connected to database
return res.sendStatus(500) // Internal server error
case UnauthorizedError:
return res.sendStatus(401) // Unauthorized
case default:
return res.status(400).send(err) // Generic error
}
}
}
Error classes:
class DatabaseConnectionError extends Error {}
class UnauthorizedError extends Error {}
Because you have that try/catch block inside your http handler method, anything that throws or rejects inside the Request.create method will be caught there. See https://repl.it/LtLo/3 for a more concise example of how errors thrown from async function or Promises doesn't need to be caught directly where they are first called from.
Having trouble figuring out how to handle exceptions here.
Container
async handleResetPassword(uuid) {
console.log("handleResetPassword invoked!")
try {
await this.props.resetUserPassword()
...rest of the code
Thunk Action
export function resetPasssord() {
return async (dispatch, getState) => {
const state = getState()
try {
const response = await UserApi.ResetUserPassword(state.auth.token)
if(response && response.body){
dispatch(UserActions.resetPasswordReceived)
}
dispatch(UserActions.resetPasswordFail)
}
catch(err) {
//what do I do here? How should I resolve this?
dispatch(UserActions.resetPasswordFail)
}
}
}
Also how to handle the related API's catch. As you see above the thunk action calls the Api here:
UserApi (uses superagent):
const ResetUserPassword = async (token) => {
try{
return await request
.post(`${endpoint}/users/recover-password`)
.set({ Authorization: `Bearer ${token}` })
.accept('application/json')
}
catch(err) {
console.log(err)
//how handle this?
}
}
Here are my thoughts when brainstorming this:
need to return some kind of promise from API call in catch so that await can resolve in thunk action
need to return some kind of promise from thunk action in catch so that container can resolve the rejection
do I dispatch an action for error in the try-catch?
but I'm not sure if I'm on the right path or what to code in each of those catches above.
Really odd issue I am having. For some reason my put in my catch block is not being executed below. Here's my saga:
function* postLoginFormSaga(action) {
let response;
try {
// Data from emitting login action
response = yield call(AuthenticationApi.login, action.formData);
yield put(createLoginSucceedAction());
yield put(push('/dashboard/summary'));
} catch (e) {
console.log(e);
yield put(createLoginFailAction(response))
}
}
And my api call, which has a custom middleware to handle non 2XX responses:
static login = (formData) => {
return fetch('/api/login', {
method: 'POST',
body: formData,
credentials: 'same-origin'
}).then(handleFetchErrorMiddleWare)
.then(r => r.json())
};
Where the middleware function is a simple function that checks the ok property of the response object:
export function handleFetchErrorMiddleWare(response) {
if (!response.ok){
throw Error(response.status)
}
return response
}
The middleware is working correctly as I can see the output of the console.log(e) when an exception is thrown, but the yield put(createLoginFailAction(response)) is not being emitted ...
See below:
Any ideas? Thanks
Seems to me like there is an error in the createLoginFailAction function, if so that would cause the result that you are seeing.