React Native, Axios and Unhandled Promise Rejections - javascript

I am using Axios in my React-Native app to communicate with a Nodejs backend, and am using react-redux dispatch to call the actions that will utilize Axios. However, no matter what I try I land up getting "Unhandled Promise Rejection" anytime there is an error of any sort. This is frustrating me so much I'm about to give up on Axios and just go back to using fetch. I've googled this problem (which seems to be very common indeed), but have not found any solid solution yet.
Here is an example of how I'm using Axios in my actions to send requests to my Nodejs backend server:
export const getAppointments = (userId) => {
return async (dispatch) => {
const request = axios
.get(`${SERVER_BOOKINGS}/upcoming_appointments/${userId}`)
.then((response) => {
let ourPayload = {};
ourPayload.success = response.data.success;
if (ourPayload.success) {
ourPayload.bookings = response.data.bookings;
dispatch({
type: GET_UPCOMING_APPOINTMENTS,
payload: ourPayload,
});
}
})
.catch((err) => {
console.log("caught an error, HERE:", err);
//throw err
});
};
};
And here is the code I'm using to call this action:
const getAppointments = async () => {
try {
await dispatch(
bookingActions.getAppointments(userObject.userData.userId)
);
} catch (error) {
console.log("we actually caught an axios error client side!");
}
}
If I leave the code exactly as above and I deliberately cause an error response from my Nodejs code , I get a console.log message saying "caught an error, HERE", but get nothing from the catch block where I actually dispatch the action (2nd block of code).
If I uncomment out the throw err line in the first catch block, I still get the console.log, still get nothing from the second catch block.... but now I get an Unhandled Promise Rejection warning.
Right now, this code works very well as long as there isn't an error, and is completely worthless anytime there is an one. In all honestly, I don't know whether the issue is one with axios, react-redux dispatch or just a failure on my part to understand the way Promises are meant to work, but I've wasted untold hours trying to figure out what should be a really simple matter of catching errors... and I'm falling behind schedule on this project because of it. Any advice/help would be greatly appreciated!

I think that the problem is caused since "bookingActions.getAppointments" is not a Promise function so there is no need to "try-catch" it.
try to change it like this:
const getAppointments = () => dispatch(bookingActions.getAppointments(userObject.userData.userId));

Related

Why line of code isn't executed after awaiting server response

In a react component I havel the following code:
 
const handleButton = async () => {
 
const resp = await updateProject(projectInfo);
setIsProjectModalVisible(false)
};
 
updateProject() is a POST request to a server and setIsProjectModalVisible is a setState function from the parent component that was passed as a prop.
I was having a strange behavior: with the previous code, the Modal wouldn't be hidden (setIsProjetModalVisible(false) wouldn't run), but if I swapped the two lines:
 
const handleButton = async () => {
 
setIsProjectModalVisible(false)
const resp = await updateProject(projectInfo);
};
it would work as expected and the Modal would become invisible.
After a bit of research, I identified the problem: the server wasn't responding to updateProject(). Fixing that, the second line of code would now result in the expected behavior.
Even though the problem is solved, I'd like to understand that behavior: since the server wasn't responding, I might expect an infinite wait in the await line, but while debugging, it would just skip that line and no error was shown. Why is that? Thanks!
When you call updateProject, it returns a Pending Promise. At first a pending promise wouldn't block the next following code setIsProjectModalVisible(false).
But when you declare your function as async, and mark await to your promise you are stating that following code should only execute when your promise resolves. If the promise hangs on pending your code is blocked until the promise resolves.
The promise could also rejects (you get a server timeout error or something else goes wrong), which would throw an error, but your code wouldn't be executed. Your app would crash, unless you had wrapped your promise in a try/catch block (which is recommended), where you would handle the error at the catch block properly.
const handleButton = async () => {
try {
const resp = await updateProject(projectInfo);
// if it rejects following code doesn't execute. jumps to catch block
setIsProjectModalVisible(false)
} catch (error) {
// handle error here
console.log(error)
}
};
Depending on how your updateProject is written, when a POST request inside it fails in some way, it probably does not resolve (i.e. you don't catch the error) and thus your const resp = await updateProject(projectInfo); never actually gets anything back. That, of course, results in this part setIsProjectModalVisible(false) never being reached. Hence your modal does not disappear.
So that should explain the behavior. As for fixing it, well, just catch the error and still resolve the function. So something like this would work:
const updateProject = (project_data) => {
return axios.post('url', project_data)
.catch(e => {
console.log(e);
return "Error occurred";
})
}
This way even when your post request bombs, your await will still get something back and won't stop the code from contunuing to execute.

Does async function try catch block can wrap a called async function that also may throw an error?

I have the following code:
const getJSON = async function(url, errorMsg = 'Something went wrong') {
return fetch(url).then(response => {
if (!response.ok) throw new Error(`${errorMsg} (${response.status})`);
return response.json();
});
};
const outerFunc = async function() {
try {
let x = await getJSON();
} catch (err) {
console.log(err);
}
}
outerFunc();
I tried to run the code inside the console and get an error:
VM60:2 Fetch API cannot load chrome://new-tab-page/undefined. URL scheme "chrome" is not supported.
I want to understand if the try/catch block that surrounds the **outerFunc ** function can catch an error that will come from the called async function (asyncFunction) , or asyncFunction should handle its error by himself. I know that in Java it's ok to "propagate" the error. What is the correct behavior in JS?
Generally, you want to catch errors at a point where you can do something useful in response to the error. Often, this is not the point where the API is called, but somewhere further up the call stack. Allowing errors to naturally propagate upwards to where they can be handled reasonably is a fine idea.
For example, in a real application, if there's a catch block which is composed solely of a console.log, that's often (not always) an indication that the error should be caught somewhere else. When there's an error, one will often want to inform the user that there was a problem, so the following sort of pattern is very common:
getAPI()
.then(populateWithResults)
.catch((error) => {
const div = document.querySelector('.errors').appendChild(document.createElement('div'));
div.textContent = 'There was an unexpected error: ' + error.message;
});
where getAPI does no catching of its own, but simply allows the caller to handle possible errors - similar to what your getJSON function is doing.
Usually you'll only want one point where an error is caught, but occasionally you might want to catch it in multiple places - for example, perhaps a sub-component wants to do something in particular when an error is encountered, but it also wants to notify the callers that there's a problem. In such cases, re-throwing the error could look like:
const makeSubComponent = () => {
const componentContainer = document.querySelector('.container');
const populateSubComponent = () => {
// ...
};
return getAPI()
.then(populateSubComponent)
.catch((error) => {
componentContainer.textContent = 'Something went wrong...'
throw error; // Inform the caller of makeSubComponent of the error
});
};
allowing both makeSubComponent and its caller to deal with possible problems.

Why can't I catch error thrown from node-postgres?

I'm having an issue catching an error thrown from the Node-Postgres NPM package.
The issue seems simple on the surface, but I've tried everything I can think of.
My code is like the following:
import { Pool } from 'pg' // Import postgres connection pool
const pgPool = new Pool()
async function queryDatabase() {
try {
// Force TypeError by passing undefined
let queryResult = await pgPool.query( undefined )
if ( queryResult.rows.length > 0 ) {
return queryResult.rows[0]
}
return false
} catch( err ) {
// Never Reached
return new Error( 'Test error' )
}
}
queryDatabase()
And the error is as follows:
TypeError: Client was passed a null or undefined query
at Client.query (~/.../node_modules/pg/lib/client.js:479:11)
The error itself is pretty self-explanatory. I'm forcing the error here, for the sake of trying to handle it in the event that undefined gets passed by mistake. I realize that I can simply perform a check to make sure the input is never null or undefined, but that's not my main concern.
My worry is if I can't catch this error thrown from this package, how many other unforeseen cases am I going to encounter where I simply can't catch and handle a thrown error.
I've tried numerous different approaches - The Async/Await Try/Catch method, shown above - I've tried pgPool.query().then().catch() - Combinations of the two. I've even tried running the catch against the Pool instance itself. No matter what I do, I can't handle the exception without using Node's process.on('unhandledRejection', ...), which is of course a bad idea.
I've been racking my brain on this for hours. Is there any way that I can catch and handle errors like this, so it's not crashing my server every time? Thanks in advance!
I was able to reproduce this and it seems to be an actual bug in the pg-library.
According to the source if you call .query on a pool instance, this instance will attempt to connect and get a client. In this connect-callback the actual query is dispatched to the client-module, which will throw the mentioned type error if the query is nil.
This error is thrown synchronously (i.e. the error is not passed to the callback argument, e.g. callback(new TypeError("...")) and since there's no try/catch around the client.query call in the pool's connect-callback, the error will not be caught by your try/catch.
A potential fix would be to wrap the client.query call in a try catch:
client.once('error', onError)
this.log('dispatching query')
try {
client.query(text, values, (err, res) => {
this.log('query dispatched')
client.removeListener('error', onError)
if (clientReleased) {
return
}
clientReleased = true
client.release(err)
if (err) {
return cb(err)
} else {
return cb(undefined, res)
}
})
}catch(err) {
return cb(err)
}
So for now, you probably should create an issue on github and wait for the bugfix or fork the repo and use above workaround, I'm afraid.

VueJS Error handling in VueX store best practices

I'm having some issues with the way im handling errors in Vue. Currently im using a try/catch block in VueX to make some async calls. The catch block handles adding the error to a global toast. this is working very well.
async add({ dispatch }, form) {
try {
await firebase.add(form)
dispatch('getUpdatedList')
} catch (error) {
// Use the global error handle to handle the error
dispatch('error', error)
}
},
error ({ dispatch, commit }, errorMessage) {
console.log(errorMessage)
commit('ERROR', errorMessage)
}
// Add the Errors here
ERROR (state, val) {
state.errors = [val, ...state.errors]
},
The issue I have is this: When that error is caught in the block, it doesn't allow the errors to propagate to the components, so I can't handle the error in the component the way I would like. For example, if the client submits a form, but somehow that fails, the then block of a promise will still execute. So i can't reset the form, or stop a redirect, or do some UI tidy up on the client.
this.$store
.dispatch('add', this.data)
.then(() => {
//This gets hit no matter what the result
this.showSuccess = true
})
.catch(() => {
// More UI stuff
//Can only hit this when rethrowing the error
this.showLoading = false
this.showRegisterButton = true
})
I know that I can get around this by rethrowing the error, but that doesn't seem like the best way forward, as I've always believed that rethrowing an error is bad practice (although here it seems like a decent solution) Is there something simple that im missing?
This should be fine, with just one amendment.
You can re-throw the error to allow parent to catch using throw error
async add({ dispatch }, form) {
try {
await firebase.add(form)
dispatch('getUpdatedList')
} catch (error) {
// Use the global error handle to handle the error
dispatch('error', error)
// throw the handled error for another handler to catch
throw error
}
}

Error handling redux-promise-middleware

I'm learning React, along with pretty much all the necessary technology around it all at once - so I often get tripped up by things I should probably know already.
I've encountered a problem when it comes to error handling my async events. I've scoured the web and nothing really answers exactly what I'm looking for.
I'm currently using redux with redux-promise-middleware to handle the async actions, like this:
export function myFunc() {
return {
type: FETCH_FUNC,
payload: new Promise((resolve, reject) => {
fetch ('some/url/location/from/which/to/fetch')
.then( response => {
if (!response.ok){
throw new Error(response);
}
resolve(response.json());
}).catch(error => {
reject(error);
}),
})
};
}
There are two things here: first, the code works just fine when no errors are present. However, when I purposely create an error in the code the correct methods are firing but I still end up with the following error in my console:
Uncaught (in promise) Error: [object Response]
Should the .catch(...) block not be handling this? What am I missing? Should I be getting this anyway? If so, why?
Secondly, I've read that wrapping the fetch inside a new Promise is an anti-pattern, and there was an almost-hint that this may be what's causing problems here. All the examples I've come across use it in this fashion. What's the alternative? How do I fire the resolve/reject to dispatch the next actions without the wrapper?
Any help will be greatly appreciated. Thanks masters of the web.
-------------EDIT 1----------------
From the official redux-promise-middleware github examples, they have the following code:
export default function request(url, options) {
return new Promise((resolve, reject) => {
if (!url) reject(new Error('URL parameter required'));
if (!options) reject(new Error('Options parameter required'));
fetch(url, options)
.then(response => response.json())
.then(response => {
if (response.errors) reject(response.errors);
else resolve(response);
})
.catch(reject);
});
}
It seems to intention with the middleware is to wrap fetch inside a new Promise and catching any rejects. If anyone has a working alternative way of implementing this using redux-promise-middleware, or can elaborate on why its following this pattern that would be greatly appreciated.
-------------EDIT 2----------------
Not sure what the intended way of implementing this is or how to avoid the Uncaught error in the promise. Simply calling Promise.reject(...) results in an uncaught error unless you include error handling functions: Promise.reject(...).then(() =>{...}, error => {...}). Including this with the middleware results in the rejected action never being dispatched. I've moved away from redux-promise-middleware till I can find a suitable fix and/or implementation.
I guess what you are getting is the expected result and this is mentioned clearly in the middleware documentation:
The middleware dispatches rejected actions but does not catch rejected
promises. As a result, you may get an "uncaught" warning in the
console. This is expected behavior for an uncaught rejected promise.
It is your responsibility to catch the errors and not the
responsibility of redux-promise-middleware.
But if you ask about best practices this is what i ended up doing from long time ago and it's working perfectly with me:
1- For some promises you can do as mentioned in the documentation:
dispatch({
type: 'FOO_ACTION',
payload: new Promise(() => {
throw new Error('foo');
})
}).catch(error => {
// catch and handle error or do nothing
});
2- To catch all rejected promises globally add this middleware before the redux-promise-middleware as follow:
/**
* a utility to check if a value is a Promise or not
* #param value
*/
const isPromise = value => value !== null && typeof value === 'object' && typeof value.then === 'function';
export default () => {
const middleWares = [];
// global error middleware
middleWares.push(() => next => action => {
// If not a promise, continue on
if (!isPromise(action.payload)) {
return next(action);
}
/**
* include a property in `meta and evaluate that property to check if this error will be handled locally
*
* if (!action.meta.localError) {
* // handle error
* }
*
* The error middleware serves to dispatch the initial pending promise to
* the promise middleware, but adds a `catch`.
*/
if (!action.meta || !action.meta.localError) {
// Dispatch initial pending promise, but catch any errors
return next(action).catch(error => {
if (config.showErrors) { // here you can decide to show or hide errors
console.log(`${action.type} unhandled rejection caught at middleware with reason: ${JSON.stringify(error.message)}.`);
}
return error;
});
}
return next(action);
});
// middleware
middleWares.push(thunk);
middleWares.push(promise());
middleWares.push(logger());
return applyMiddleware(...middleWares);
}
i guess this is exactly what you are looking for ;)
Extra I highly recommend axios over fetch for the following reasons:
the axios module automatically reject the promise if the request has an error code which is something you need to keep manually handle in fetch
in axios you can create instance with default base-url,header,interceptors ...
in axios you can cancel any previous request using a token this is extremely useful specially for autocomplete and chat applications
also axios internally automatically switch between xhr and http modules to perform the ajax request based on the environment (NodeJs or Browser), i personally used the same redux actions in electron, nodejs, browser and react-native and it's all working fine
Following up on caisah 's comment, get rid of the indirection. You can resolve or reject a promise by simply resolving or rejecting with a new promise object
export function myFunc() {
return {
type: FETCH_FUNC,
payload: fetch ('some/url/location/from/which/to/fetch')
.then(response => {
if (!response.ok){
throw new Error(response);
}
return Promise.resolve(response.json());
}).catch(error => {
return Promise.reject(error)
}),
})
};
}
myFunc().payload.then(json => /* do stuff with json*/)
P.S the returns may be redundant.
I’ve used "Catching Errors Globally" presented in "Catching Errors Thrown by Rejected Promises", as shown, when calling applyMiddleware the errorMiddleware should be before the promiseMiddleware. To filter the action types where to apply this middleware i've preferred a regex:
This is the store creation:
import { createStore, combineReducers, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise-middleware';
import errorMiddleware from './errorMiddleware';
import adultosReducer from './adultosReducer';
const rootReducer = combineReducers({
adultosReducer
});
const composeStoreWithMiddleware = applyMiddleware(errorMiddleware, promiseMiddleware())(
createStore
);
export default composeStoreWithMiddleware(rootReducer);
This is the error middleware:
import isPromise from 'is-promise';
import _ from 'lodash';
const tiposAction = /^ADULTO/i;
export default function errorMiddleware() {
return next => action => {
// If not a promise, continue on
if (!isPromise(action.payload)) {
return next(action);
}
console.log('errorMiddleware: action.type', action.type);
if (action.type.match(tiposAction)) {
// Dispatch initial pending promise, but catch any errors
return next(action).catch(error => {
console.log('catching action', error);
return error;
});
}
return next(action);
};
}
That way you show gently to the user the error because the rejected action is dispatched without the Unhandled promise. And of course there is no need to add redux-thunk.

Categories

Resources