async / await is not waiting for the response - react-native

I am requesting an api using POST method. but after calling the RemoteRequest function using await. It's not waiting for the response return. It directly executes the remaining code in the register.js by this im getting the status = undefind in the console.log.
register.js
const DeviceUniqueId = DeviceInfo.getUniqueId();
const requestBody = { phone: phone, username: username };
const status = await RemoteRequest(URLs.APP_REGISTER,
'POST', DeviceUniqueId, requestBody);
console.log('status====>', status);
this.setState({
loading : false
});
remotereuqest.js
export const RemoteRequest = async (url, method, DeviceUniqueId, requestbody) => {
console.log(url, method, DeviceUniqueId, requestbody);
NetInfo.fetch().then((state) => {
if (state.isConnected) {
fetch(url, {
method : method,
headers : {
Accept : 'application/json',
'Content-Type' : 'application/json',
DEVICEID : DeviceUniqueId,
'Accept-Charset' : 'utf-8'
},
body : JSON.stringify(requestbody)
})
.then((response) => {
console.log('reponse=====>', response);
return response.json();
})
.then((responseData) => {
console.log(responseData);
if (responseData.status == 'OK' && responseData.code == 200) {
return responseData.code;
}
return null;
})
.catch((error) => {
console.log(error);
if (error.message == 'Network request failed') {
showMessage({
floating : true,
message : 'Connection error',
description : 'you have no Internet Connection',
type : 'alert',
backgroundColor : 'red'
});
return null; //503
}
else {
showMessage({
floating : true,
message : 'Internal Server Error',
description : 'please try again after some time',
type : 'alert',
backgroundColor : 'red'
});
throw error;
return null;
}
})
.done();
}
else {
showMessage({
floating : true,
message : 'Connection error',
description : 'you have no Internet Connection',
type : 'alert',
backgroundColor : 'red'
});
return null; //503
}
});
};

You need to await for all promises within the function, otherwise they still get executed asynchronously. Something like this:
await NetInfo.fetch().then( async (state) => {
if (state.isConnected) {
await fetch(url, {
...
}
}
});

When you use .then() the code after it gets executed immediately, so instead you should await the responses and then do the work without .then()-s.
const state = await NetInfo.fetch();
if (state.isConnected) {
const response = fetch(url, ...);
console.log(response);
...
}

Related

react native make accessToken global

im using rn-secure-storage to save authState when using oauth2, then i have class AppHelper to control all network function like below:
import RNSecureStorage, { ACCESSIBLE } from 'rn-secure-storage'
export const accessToken = async() => {
await RNSecureStorage.get("authState").then((value) => {
console.log("authState", value);
return JSON.parse(value).accessToken
}).catch((err) => {
console.log("can not get authState", err);
});
};
export const getData = (url, tag = 'getData') => {
return fetch(url, {
method : 'GET',
headers: {
'Content-Type': 'application/json;charset=utf-8',
'Authorization' : 'Bearer ' + accessToken
}
})
.then((response) => {
console.log(tag, 'Bearer ' + accessToken);
return response.text();
})
.then((json) => {
console.log(tag, json)
return json;
})
.catch((error) => {
handleError(err)
console.log(tag, error, url)
});
}
export const handleError = (error) => {
if (error.response.code == 401){
alert('Unauthorized')
}else{
console.log('network call failed', error)
}
}
im coding native before, so im not familiar with React native syntax. I just want to get access token to apply in network call, but my code show error:
_getMasterInfoApi Bearer function _callee() {
return _regenerator.default.async(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return _regenerator.default.awrap(_rnSecureStorage.default.get("authState").then(function (value) {
console.log("authState", value);
return JSON.parse(value).accessToken;
}).catch(function (err) {
handleError(err);
}));
case 2:
case "end":
return _context.stop();
}
}
}, null, null, null, Promise);
}
Can anyone help? thanks in advance

How to passing result of http request inside async in ExpressJS?

I have below code
async send(user, data) {
const postData = {
'data': 'john',
'secret': 'secret'
};
const dataJson = JSON.stringify(postData);
const options = {
hostname: 'example.com',
path: '/send',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': dataJson.length
}
};
const req = https.request(options, (res) => {
let data = '';
console.log('Status Code:', res.statusCode);
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('Body: ', JSON.parse(data));
});
}).on("error", (err) => {
console.log("Error: ", err.message);
});
req.write(dataJson);
req.end();
//---------------
let postResult = // HERE I WANT TO GET WHAT HTTP POST REQUESTED (e.g dataJson.body?)
//---------------
let result;
try {
result = await this.users.collection('users').updateOne(
{
_id: user
},
{
$set: {
// I WANT TO USE THAT HERE
data1 : postResult,
data2 : data2
}
},
{ maxTimeMS: consts.DB_MAX_TIME_USERS }
);
} catch (err) {
log.error('DB', 'UPDATEFAIL id=%s error=%s', user, err.message);
err.message = 'Database Error, failed to update user';
err.code = 'InternalDatabaseError';
throw err;
}
return { success: true };
}
How to get those data to outside variable?
I almost crazy about this, been searching on google and not found anything
I am using express and native-http to make http request, are there any native-curl maybe?
thank you very much for all the help
Your current code is using callback to retrieve result, so you can initiate data variable to outside callback function
let data = '';
const req = https.request(options, (res) => {
console.log('Status Code:', res.statusCode);
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('Body: ', JSON.parse(data));
});
})
And also there are other easier way to make http request with nodejs. you can check axios that support Promise and async/await.
you can use syntax like this with axios
const response = await axios.get('/user?ID=12345');
way more easier.

How to repeat action with query, in case of authentication issues?

I have get menu action with authentication.
async getMenu({rootState, commit}) {
try {
const { auth, lang } = rootState;
const {items} = await this.$axios.$get(`/api/${ lang.locale }/menu`, {
headers: {
'Authorization': `bearer ${auth.token}`,
'Accept-Language': `${lang.locale}`
},
});
if (items) {
// set items
commit('setMenu', items);
}
} catch (error) {
console.log({Error: error})
}
}
In case of error, I submit request with refresh token
$axios.onError(async (error) => {
const code = parseInt(error.response && error.response.status);
const message = error.response && error.response.data && error.response.data.error;
if (code === 403) {
await store.dispatch(
'auth/refreshToken',
{ refreshToken: store.state.auth.refreshToken },
{ root: true }
);
How to repeat action after get token ?
Use
$axios.defaults.validateStatus = (status) => {
return [200, 402].indexOf(status) !== -1;
};
$axios.onResponse(async (response) => {...}

Firebase Cloud Functions Call : error : Object message : "Bad Request" status : "INVALID_ARGUMENT"

first of all i am working with react-native
i wanted to use Custom Claims on my project since it seems to fit the role distribution i expect to use on my app.
after setting my app following the documentation i succeed on creating some functions
but, here's the thing, when i try to call a function by fetching the endpoint i always get this error :
in the console
error
:
Object
message
:
"Bad Request"
status
:
"INVALID_ARGUMENT"
in firebase console
addAdminRoleTest Request body is missing data. { email: 'dev#test.com' }
i couldn't find any answer to that except that i send wrong information from my fetch but i don't understand why.
i even tried to simplify my function only to get the data i sent but i had the exact same error
find below my cloud function & the calling method :
functions/index.js
exports.addAdminRole = functions.https.onCall((data, context) => {
// get user
return admin.auth().getUserByEmail(data.email).then(user => {
// if not already (admin)
if(user.customClaims && (user.customClaims).admin === true) {
return;
}
// add custom claim (admin)
return admin.auth().setCustomUserClaims(user.uid, {
admin: true
});
}).then(() => {
return {
message: `Bravo : ${data.email} fait partie de l'équipe Admins`
}
}).catch(err => {
return err;
});
});
simplified function :
exports.addAdminRoleTest = functions.https.onCall(data => {
console.log("parse data : "+JSON.parse(data));
return (
JSON.parse(data)
);
});
adminScreen.js
function httpAddAdminRole() {
const initRequest = {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body:JSON.stringify({
email: 'dev#test.com'
})
}
console.log(initRequest);
return fetch('https://us-central1-my-project.cloudfunctions.net/addAdminRole', initRequest)
.catch(err => console.log(err))
.then(res => res.json())
.then(parsedRes => {
console.log(parsedRes);
});
}
in the end this was mostly json knowledge that missed me
my body should have data included
here's the answer i came to :
functions/index.js
exports.addAdminRole = functions.https.onCall((data, context) => {
const dataParsed = JSON.parse(data);
// get user
return admin.auth().getUserByEmail(dataParsed.email).then(user => {
// if not already (admin)
if(user.customClaims && (user.customClaims).admin === true) {
console.log(dataParsed.email + " is already an Admin");
return;
}
// add custom claim (admin)
return admin.auth().setCustomUserClaims(user.uid, {
admin: true
});
}).then(() => {
return {
message: `Bravo : ${dataParsed.email} is now an Admin`
}
}).catch(err => {
return err;
});
});
adminScreen.js
function httpAddAdminRole(mail) {
const initRequest = {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body:JSON.stringify({
data:JSON.stringify({
email: mail
})
})
}
console.log(initRequest);
return fetch('https://us-central1-my-project.cloudfunctions.net/addAdminRole', initRequest)
.catch(err => console.log(err))
.then(res => res.json())
.then(parsedRes => {
console.log(parsedRes);
});
}

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.