Unhandled Promise Rejection when trying to call external function from async function - react-native

The error message:
WARN Possible Unhandled Promise Rejection (id: 1):
Error: INVALID_STATE_ERR
send#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:31745:26
initialiseWebsocket#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:100544:21
loadUserData$#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:100610:40
tryCatch#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7739:23
invoke#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7912:32
tryCatch#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7739:23
invoke#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7812:30
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7822:21
tryCallOne#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:28596:16
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:28697:27
_callTimer#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:29113:17
_callImmediatesPass#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:29152:17
callImmediates#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:29370:33
__callImmediates#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3279:35
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3057:34
__guard#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3262:15
flushedQueue#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3056:21
flushedQueue#[native code]
invokeCallbackAndReturnFlushedQueue#[native code]
The useEffect that is being accused of being a problem:
React.useEffect(() => {
// Fetch the token from storage then navigate to our appropriate place
const loadUserData = async () => {
let userData;
try {
userData = await retrieveUserData();
} catch (e) {}
if(userData){
dispatch({ type: 'RESTORE_USER_DATA', userData: userData });
getChatData(userData, setChats, dispatch);
if(userData && !websocketInitialised){
console.log('web init called from *load user data*')
setWebsocketInitialised(true)
initialiseWebsocket(userData);
}
}
else{
dispatch({ type: 'RESTORE_USER_DATA_FAILED'});
}
};
loadUserData();
}, []);
The initialliseWebsocket function
function initialiseWebsocket(userData){
console.log('sending websocket initialisation data.');
websocket.send(JSON.stringify({
'action': 'init',
'data' : {'token': userData.token}
}));
}
the useState that is used above
const [websocketInitialised, setWebsocketInitialised] = React.useState(false);
async function getChatData(userData, setChats, dispatch){
console.log("fetching chat data");
// if we fail to download chat data, pull the old one from FS
const loadOldChatData = async () => {
let chats;
try {
chats = await retrieveChats();
} catch (e) {}
if(chats){
setChats(chats);
console.log("loaded cached chat data") ;
}
else{
setChats([]);
}
};
const onSuccess = (response) => {
if(response['chats']){
storeChats(response['chats']);
setChats(response['chats']);
console.log("chat data synced");
}
else{
loadOldChatData();
}
};
const onFailure = (response) => {
loadOldChatData();
};
fetch(Settings.siteUrl + '/messenger/get_chats/', {
method: "GET",
headers: {
"Content-type": "application/json; charset=UTF-8",
"Authorization": "Token " + userData.token
},
})
.then(response => response.json())
.then(response => {onSuccess(response)})
.catch(response => {onFailure(response)})
}
retrieveUseData() is most likely not the problem as this only started occuring after I added the other code.
Am I not supposed to use states like this or am I supposed to use the async key worked on functions? I tried that but I still have the same issue. You can see on the 4 line of the errors it mentions the 'initialiseWebsocket' function. I am guessing that is the route cause. I assume the solution will be some async version of it...

This error tell us that you didn't or forget to handle error from async code.
I refectory your code a bit, Tell me if you got any error message from console.log(error);
React.useEffect(() => {
// Fetch the token from storage then navigate to our appropriate place
(async () => {
try {
let userData = await retrieveUserData();
dispatch({ type: 'RESTORE_USER_DATA', userData });
await getChatData(userData, setChats, dispatch);
if (websocketInitialised) return;
console.log('web init called from *load user data*')
setWebsocketInitialised(true)
initialiseWebsocket(userData);
} catch (error) {
console.log(error);
dispatch({ type: 'RESTORE_USER_DATA_FAILED' });
}
})();
}, []);
And you should rename getChatData to setChatData, I also simplify those code also...
async function getChatData(userData, setChats, _dispatch) {
try {
let response = await fetch(Settings.siteUrl + '/messenger/get_chats/', {
method: "GET",
headers: {
"Content-type": "application/json; charset=UTF-8",
"Authorization": "Token " + userData.token
},
}),
data = await response.json(),
chats = data['chats'];
if (!chats?.length) throw "empty chat data, pull the old one from FS";
storeChats(chats);
setChats(chats);
} catch (_) {
// if we fail to download chat data, pull the old one from FS
await retrieveChats()
.then(chats => setChats(chats))
.catch(() => setChats([]))
}
}
"I don't really understand what you are doing with the async stuff."
async/await is just syntax sugar of promise, It allow you to work with async operation in a synchronous manner, some rules of async/await
In other to use await keyword, you need an async function.
you can make any function asynchronous, just by adding async keyword
async function always return promise
Lets see an example:
let delay = (ms, msg, bool) => new Promise((res, rej) => setTimeout(!bool ? res : rej , ms,msg));
This helper function create a promise for our example, it take 3 arguments, it take millisecond as 1st arg, to delay, 2rd is the message as payload. 3nd is Boolean; it true, then it will reject.
let delay = (ms, msg, bool) => new Promise((res, rej) => setTimeout(!bool ? res : rej, ms, msg));
let log = console.log;
async function myAsyncFn() {
let hello = await delay(100, "hello,");
let world = await delay(300, " world!");
// we use a symbol '#' to indicate that, its from `myAsyncFn`
log("#" , hello + world, "printed from async operation");
}
myAsyncFn();
log("As you can see that, this message print first");
// we are creating an async function and called immediately, In other to use `await keyword`
(async () => {
try {
let resolved = await delay(300,"resolved");
console.log(">" , `it ${resolved}!`);
// this will reject and catch via `try/catch` block;
let _ = await delay(600, "Error", true);
log("It will not print!");
// ...
} catch (error) {
log(">" , `we can catch "${error}" with try/catch, as like any sync code!`);
}
})()
As you can see that with async/await its look like everything is synchronous right? even everything execute asynchronously!
You just need to use await keyword to make every async operation synchronous.

Related

Struggling to correctly make use of returned values from functions that are being called Asynchronously by Axios

I have an Express server which serves as an API Request forwarding tool (i.e my client calls the express server, and the express server forwards that call to another API).
The way this server is supposed to work is that there is a single entry point which makes a request, and then based on the response makes further API requests and returns a result based on the combo of API request responses.
To be clearer, the main logic is as follows:
Single entry point, which makes an async axios call to get an ID value
Within this function, we call an async function, getPartnerDetails (passing that ID as the parameter)
getPartnerDetailscalls a 3rd async function, '''getRawJson''' which is supposed to return the final required result, passing it back to '''getPartnerDetails''' which then passes it to the main entry point.
Whats going wrong is that the results are being recieved but are not being pass back correctly. The console logs within the '''.then(()=>{})''' of my async functions are coming back as '''undefined'''.
Code below:
app.post('/checkuser', async (req, res, next) => {
const { man, bfn, bln, bsn, bc, bs, bz, bco } = req.body;
const bodyContent = {
man: man,
bfn: bfn,
bln: bln,
bsn: bsn,
bc: bc,
bs: bs,
bz: bz,
bco: bco,
profile: 'DEFAULT',
};
try {
await axios
.post('https://host.com/im/account/consumer', bodyContent, { headers })
.then((response) => {
const addressValidResult = response.data.score.etr.filter(
(result) => result.test === '19:2'
)[0];
// console.log(res.json(result.details));
const requestId = response.data.mtid;
const currentValidAddress = getPartnerDetails(requestId).then(
(result) => {
console.log('this is currentvalidaddress ' + result);
res.json({
validationMessage: addressValidResult,
currentValidAddress: result,
});
}
);
})
.catch((err) => next(err));
} catch {}
});
async function getPartnerDetails(appId) {
let config = {
headers: {
'Content-Type': 'application/json',
Authorization: 'Basic M2QzMsdfsslkfglkdjfglkdjflkgd',
},
params: {
partner: 19,
},
};
const res = await axios
.get(
`https://host.com/im/account/consumer/${appId}/partner/requests`,
config
)
.then((response) => {
const requestId = response.data.requests[0].request_id;
console.log('this is request id ' + JSON.stringify(requestId));
const raw = getRawJson(appId, requestId).then((result) => {
console.log('this is getRawJson result ' + JSON.stringify(result));
return JSON.stringify(result);
});
// https://host.com/im/account/consumer/:appId/partner/request/:requestId
})
.catch((err) => console.log('err2' + err));
}
async function getRawJson(appId, requestId) {
const res = await axios
.get(`host.com/im/account/consumer/${appId}/partner/request/${requestId}`, {
headers,
})
.then((response) => {
console.log('this is response ' + JSON.stringify(response.data));
return JSON.stringify(response.data);
})
.catch((err) => console.log('err1 ' + err));
}
It might have something to do with how I'm using async and await, I'm new to it so I'm hoping that I'll learn a thing or 2 more about it by solving this project.
I'm also aware that maybe I should split the entry point out into 3 different entry points, and have the client manage the chaining of the requests and responses instead.
Thanks!!
Probably an error due to incorrect async await usage.
Try to change your code like this:
app.post('/checkuser', async (req, res, next) => {
const { man, bfn, bln, bsn, bc, bs, bz, bco } = req.body;
const bodyContent = {
man: man,
bfn: bfn,
bln: bln,
bsn: bsn,
bc: bc,
bs: bs,
bz: bz,
bco: bco,
profile: 'DEFAULT',
};
try {
const { data } = await axios.post(
'https://host.com/im/account/consumer',
bodyContent,
{ headers }
);
const addressValidResult = data.score.etr.filter(
(result) => result.test === '19:2'
)[0];
const requestId = data.mtid;
const currentValidAddress = await getPartnerDetails(requestId);
console.log('this is currentvalidaddress ' + currentValidAddress);
res.json({
validationMessage: addressValidResult,
currentValidAddress: currentValidAddress,
});
} catch (err) {
next(err);
}
});
async function getPartnerDetails(appId) {
let config = {
headers: {
'Content-Type': 'application/json',
Authorization: 'Basic M2QzMsdfsslkfglkdjfglkdjflkgd',
},
params: {
partner: 19,
},
};
const { data } = await axios.get(
`https://host.com/im/account/consumer/${appId}/partner/requests`,
config
);
const requestId = data.requests[0].request_id;
console.log('this is request id ' + JSON.stringify(requestId));
return getRawJson(appId, requestId);
}
function getRawJson(appId, requestId) {
return axios
.get(`host.com/im/account/consumer/${appId}/partner/request/${requestId}`, {
headers,
})
}

In reactnative expo I tried using secureStore from expo in redux to save token the one I get from api

I tried using redux to save token the one I get from api in react native ..its working now.
First one is for settoken and other one is for gettoken.
enter image description here
export const verifyOTP = (formValues, actions) => {
return async (dispatch) => {
dispatch(startSubmitting());
const url = `/validate-otp`;
var formdata = new FormData();
formdata.append("mobile", formValues.mobile);
formdata.append("otp", formValues.otp);
const response = await api.post(url, formdata);
dispatch({
type: "VERIFY_OTP",
payload: response,
});
dispatch(stopSubmitting());
await SecureStore.setItemAsync("userToken", response.data.access_token);
};
};
export const checkUser = () => {
return async (dispatch) => {
const token = await SecureStore.getItemAsync("userToken");
const url = `/me`;
const response = await api
.post(url, { token })
.then((res) => {
return res;
})
.catch((error) => {
return error.response;
});
dispatch({
type: "CHECK_USER",
payload: response,
});
};
};
The Problem
you are mixing two different implementations in checkUser to handle a promise which is clearly incorrect and leads to the issues.
The Solution
since your other parts of codes use the async/await so try to remove then/catch block from the response constant:
const checkUser = () => {
return async (dispatch) => {
const url = '/me';
try {
const token = await SecureStore.getItemAsycn("userToken);
const response = await api.post(url, {token})
dispatch({type: "CHECK_USER", payload: response})
} catch (error) {
// to proper action on failure case
}
}
}
Note 1: always use async/await in try/catch block. more on MDN documentation.
Optional
since you are trying to call two async actions (once for getting token and once for calling '/me' API), I encourage you to use two different try/catch blocks to handle the failure case for each async action separately. for example:
const checkUser = () => {
return async (dispatch) => {
let token = null;
try {
token = await SecureStore.getItemAsync("userToken");
} catch (err) {
// proper action in case of failure on getting the token from storage
}
// you may need to ignore API calls without the token, so:
try {
if(token){
const url = '/me';
const response = await api.post(url, {token});
dispatch({type: "CHECK_USER", payload: response});
}
} catch (err) {
// take proper action with the error response according to your applicaiton
}
}
}

Error handling in Express using Axios for API call

I am trying to set up a basic express app to get some API data using axios. I want to do things the right way but I am a bit lost with error handling. Ideally, if there is an error I want to communicate it to users which I could do if the API call was within it the route. But how do you do it if it's a separate function?
axios call function using async:
const getForm = async () => {
try {
const config = {
method: 'get',
url: 'https://api.something.org/niceform'
}
}
const response = await axios(config)
return response
} catch (error) {
return error.message
}
}
express route:
app.get('/niceform', async (req, res) => {
try {
const data = await getForm()
res.send(data)
} catch (error) {
???
}
})
If I understand it correctly the getForm() function will return either the response or the error and then the route will send whatever comes back. But then what does the route's catch block do and how should I use it?
Is this setup considered to be a good practice?
Any advice would be appreciated, I am still learning.
The catch block can be removed from the getForm function. An error will be caught anyways in the get route.
const getForm = async () => {
const config = {
method: 'get',
url: 'https://api.something.org/niceform'
};
const response = await axios(config);
return response;
}
Or the error can be caught inside getForm, in order to do something in that catch block, and be thrown:
const getForm = async () => {
const config = {
method: 'get',
url: 'https://api.something.org/niceform'
};
try {
const response = await axios(config);
return response;
} catch (err) {
// log the error
// add extra information to the error
// else
// (see the attached answer)
throw err;
}
}
Consequently, in the catch block in the get route, an error can be responded:
app.get('/niceform', async (req, res) => {
try {
const data = await getForm();
res.send(data);
} catch (error) {
res.error(error);
}
})
Reference:
https://stackoverflow.com/a/42171508/3563737
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw

React-Native async function unexpected identifier _this2

I have this function that I want to wait of the result of and then use it:
getUserId = () => {
fetch("https://www.dummysite.com/mobile/person-id", {
credentials: "include",
method: "GET",
headers: {
Cookie: this.state.auth_token_res
}
}).then(res => {
let id_obj = JSON.parse(res._bodyText);
console.log("parsed json", id_obj);
return id_obj.data;
});
};
I want to use it in this function:
async sendID() {
let user_id = await this.getUserId();
console.log(user_id);
OneSignal.sendTags({
user_id: user_id
})
.then(function(tagsSent) {
// Callback called when tags have finished sending
console.log("tag is set: ", tagsSent);
})
.catch(err => {
console.log("error", err);
});
}
I don't see any syntax problems, and the app compiles, but when it starts it just hows this error:
error image
the other weird this is if i turn on remote debugging on this screen I get a different error:
error 2
here is says that await is not in an async function but it is, and I am not getting syntax error in my editor or in the metro bundler.
There a few things you might have missed. Consider these changes. Although I didn't get a chance to test it, I am confident it will work or at least put you on a right track.
getUserId = () => {
// return fetch in order to await
return fetch("https://www.dummysite.com/mobile/person-id", {
credentials: "include",
method: "GET",
headers: {
Cookie: this.state.auth_token_res
}
}).then(res => res.json());
};
// make this an arrow function
sendID = async () => {
try {
let user_id = await this.getUserId();
// after printing then decide what to do here;
console.log(user_id);
const tagsSent = await OneSignal.sendTags({
user_id: user_id
});
console.log(tagsSent);
} catch (err) {
console.log(err);
}
}

Doing a Timeout Error with Fetch - React Native

I have a user login function that is working. But, I want to incorporate a time out error for the fetch. Is there a way to set up a timer for 5 seconds or so that would stop trying to fetch after such a time? Otherwise, I just get a red screen after a while saying network error.
_userLogin() {
var value = this.refs.form.getValue();
if (value) {
// if validation fails, value will be null
if (!this.validateEmail(value.email)) {
// eslint-disable-next-line no-undef
Alert.alert('Enter a valid email');
} else {
fetch('http://51.64.34.134:5000/api/login', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
timeout: 5000,
body: JSON.stringify({
username: value.email,
password: value.password,
}),
})
.then((response) => response.json())
.then((responseData) => {
if (responseData.status == 'success') {
this._onValueChange(STORAGE_KEY, responseData.data.token);
Alert.alert('Login Success!');
this.props.navigator.push({name: 'StartScreen'});
} else if (responseData.status == 'error') {
Alert.alert('Login Error', responseData.message);
}
})
.done();
}
}
}
I have made a ES6 function that wraps ES fetch into a promise, here it is:
export async function fetchWithTimeout(url, options, timeout = 5000) {
return Promise.race([
fetch(url, options),
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout))
]);
}
Here is how to use it:
const requestInfo = {
method,
headers,
body,
};
const url = 'http://yoururl.edu.br'
let data = await fetchWithTimeout(url, requestInfo, 3000);
// Wrapper function for fetch
const fetchSomething = async () => {
let controller = new AbortController()
setTimeout(() => controller.abort(), 3000); // abort after 3 seconds
const resp = await fetch('some url', {signal: controller.signal});
const json = await resp.json();
if (!resp.ok) {
throw new Error(`HTTP error! status: ${resp.status}`);
}
return json;
}
// usage
try {
let jsonResp = await fetchSomthing();
console.log(jsonResp);
} catch (error) {
if (error.name === 'AbortError') {
console.log('Network Error');
} else {
console.log(error.message);
}
}
I think using AbortController is the recommended way to abort a fetch call. The code snippet above handles the following scenarios:
If network is good but HTTP returns an error status, the message "HTTP error! ..." will be logged.
If network is down, setTimeout would trigger the AbortController to abort fetch after three seconds. The message "Network Error" will be logged.
If network is good and HTTP response is good, the response JSON will be logged.
The documentation for using AbortController to abort fetch is here.
There is no standard way of handling this as a timeout option isn't defined in the official spec yet. There is an abort defined which you can use in conjunction with your own timeout and Promises. For example as seen here and here. I've copied the example code, but haven't tested it myself yet.
// Rough implementation. Untested.
function timeout(ms, promise) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject(new Error("timeout"))
}, ms)
promise.then(resolve, reject)
})
}
timeout(1000, fetch('/hello')).then(function(response) {
// process response
}).catch(function(error) {
// might be a timeout error
})
Another option would be to modify the fetch.js module yourself to add a timeout that calls abort as seen here.
This is what I did to go around it:
(This is the "generic" function I use to make all calls on my app)
I created a timeout function, that will be triggered unless it is cleared before, then I clear this timeout on server response
const doFetch = (url, callback, data) => {
//... creating config obj here (not relevant for this answer)
var wasServerTimeout = false;
var timeout = setTimeout(() => {
wasServerTimeout = true;
alert('Time Out');
}, 3000);
fetch(HOST + url, config)
.then((response) => {
timeout && clearTimeout(timeout); //If everything is ok, clear the timeout
if (!wasServerTimeout) {
return response.json();
}
})
.then((response) => {
callback && callback(response.data || response);
})
.catch((err) => {
//If something goes wrong, clear the timeout
timeout && clearTimeout(timeout);
if (!wasServerTimeout) {
//Error logic here
}
});
};
I solved this problem by using a race between 2 promises, written as a wrapper around fetch. In my case I expect the request to return json so also added that. Maybe there is a better solution, but this works correctly for me!
The wrapper returns a promise which will resolve as long as there are no code errors.
You can check the result.status for 'success' and read json data from result.data. In case of error you can read the exact error in result.data, and display it or log it somewhere. This way you always know what went wrong!
var yourFetchWrapperFunction = function (
method,
url,
headers,
body,
timeout = 5000,
) {
var timeoutPromise = new Promise(function (resolve, reject) {
setTimeout(resolve, timeout, {
status: 'error',
code: 666,
data:
'Verbinding met de cloud kon niet tot stand gebracht worden: Timeout.',
});
});
return Promise.race([
timeoutPromise,
fetch(connectionType + '://' + url, {
method: method,
headers: headers,
body: body,
}),
])
.then(
(result) => {
var Status = result.status;
return result
.json()
.then(
function (data) {
if (Status === 200 || Status === 0) {
return {status: 'success', code: Status, data: data};
} else {
return {
status: 'error',
code: Status,
data: 'Error (' + data.status_code + '): ' + data.message,
};
}
},
function (response) {
return {
status: 'error',
code: Status,
data: 'json promise failed' + response,
};
},
)
.catch((error) => {
return {status: 'error', code: 666, data: 'no json response'};
});
},
function (error) {
return {status: 'error', code: 666, data: 'connection timed out'};
},
)
.catch((error) => {
return {status: 'error', code: 666, data: 'connection timed out'};
});
};
let controller = new AbortController()
setTimeout( () => {
controller.abort()
}, 10000); // 10,000 means 10 seconds
return fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify(param),
signal: controller.signal
})
I may be late but i made a code which is 100% working to timeout an API request using fetch.
fetch_timeout(url, options) {
let timeout = 1000;
let timeout_err = {
ok: false,
status: 408,
};
return new Promise(function (resolve, reject) {
fetch(url, options)
.then(resolve, reject)
.catch(() => {
alert('timeout.');
});
setTimeout(reject.bind(null, timeout_err), timeout);
});
}
You just need to pass the api-endpoint to the url and body to the options parameter.