Removing chaining promises - javascript

New to react-native and at the moment I'm working on chaining promises.
myFunction(human, destination = null) {
const { navigation } = this.props;
const { onRefresh } = navigation.state.params;
this.setState({ isLoading: true });
return PeopleService.closeService(
human.humanId,
destinationPoint && destinationPoint.humanId,
)
.then((result) => {
if (result) {
PeopleHelperService.refreshInfo().then(() => {
if (onRefresh) {
onRefresh();
}
navigation.popToTop();
PopUp.showSuccess(
"Success message",
);
});
}
PopUp.showError(
"Failing message",
);
return null;
})
.finally(() => this.setState({ isLoading: false }));
}
Things that I want to achieve is removing chain responsibility and make it simple without chaining.
Could anyone guide me on how can I achieve this? Links to some documentation and other source will be very helpful for me to understand how to make it.
UPDATE:
Seems to be the answer with async/await working.

If you don't want using promise then using async await. Here it is.
myFunction = async (human, destination = null) => {
const { navigation } = this.props;
const { onRefresh } = navigation.state.params;
this.setState({ isLoading: true });
let result = await PeopleService.closeService(
human.humanId,
destinationPoint && destinationPoint.humanId,
);
if (result) {
await PeopleHelperService.refreshInfo();
if (onRefresh) {
onRefresh();
}
navigation.popToTop();
PopUp.showSuccess(
"Success message",
);
}
PopUp.showError(
"Failing message",
);
this.setState({ isLoading: false })
}

Related

Update data in Vue.js Mounted() doesn't work

I try to update some data in my mounted part, in my Vue.js component. Here is my code :
methods: {
initData() {
const self = this;
axios
.get("/checkAuthentification")
.then((response) => {
(self.userFields.userAuthenticated = response.data),
console.log("method"),
console.log(self.userFields.userAuthenticated),
console.log("reponse"),
console.log(response.data);
})
.catch((error) => {
(self.errors = error.response.data.errors || {}),
console.log(self.errors);
});
console.log("between");
console.log(self.userFields.userAuthenticated);
if (self.userFields.userAuthenticated == "true") {
axios
.get("/getAuthenticatedUserData")
.then((response) => {
(self.userId = String(response.data.id)),
(self.userFields = response.data),
console.log("user data"),
console.log(response.data);
})
.catch((error) => {
(self.errors = error.response.data.errors || {}),
console.log(self.errors);
});
}
},
},
mounted() {
this.initData();
},
In the first axios call, it works perfectly, the two console log give the expected value "true". But on the next console.log after between, it says 'undefined'. Thue userFields doesn't update after the firt axios call. I don't understand because i did the same thing on a submit button on a form, and it worked perfectly.
I ask here because I looked other posts and the answer is always to use const=this, which I did.
Thank you for your help !
To answer your questions first you would have to understand the nature of asynchronous operations in Javascript. I will try my best to explain but will leave this link in case you would like to read more about it
On your example you have:
axios.get('/checkAuthentification').then(response =>{
console.log('CHecked authentication')
...
})
console.log('between');
console.log(self.userFields.userAuthenticated);
When you do the axios.get what happens is that an asynchornous operation will be called (asynchornous = won't wait for the answer for the code to continue execution). You will contact your backend server and ONLY when you receive an answer the console.log('checked authentication') will run but in the meantime (remember axios.get is asynchronous) the console.log('between'); AND console.log(self.userFields.userAuthenticated); will execute before you receive an answer from the backend server.
How can we solve this
We can solve this with two approaches. The first one would be the more old approach - waiting for promises to resolve by the then keyword
axios
.get('/checkAuthentification')
.then(response =>{
console.log('CHecked authentication')
console.log('between');
console.log(self.userFields.userAuthenticated);
return response;
})
.then(() => {
// do more operations after promise was resolved
})
Or we can do a more modern way - we can use async/await.
async initData() {
const response = await axios.get('/checkAuthentification');
console.log('CHecked authentication')
console.log('between');
console.log(self.userFields.userAuthenticated);
}
state: {
userFields: {
userAuthenticated: false,
...
}
},
methods: {
initData() {
const self = this;
axios
.get("/checkAuthentification")
.then((response) => {
(self.userFields.userAuthenticated = response.data),
console.log("method"),
console.log(self.userFields.userAuthenticated),
console.log("reponse"),
console.log(response.data);
})
.catch((error) => {
(self.errors = error.response.data.errors || {}),
console.log(self.errors);
});
},
getUserData() {
if (this.userFields.userAuthenticated) {
axios
.get("/getAuthenticatedUserData")
.then((response) => {
(self.userId = String(response.data.id)),
(self.userFields = response.data),
console.log("user data"),
console.log(response.data);
})
.catch((error) => {
(self.errors = error.response.data.errors || {}),
console.log(self.errors);
});
}
}
},
watch: {
'userFields.userAuthenticated': function() {
this.getUserData()
}
},
mounted() {
this.initData();
},

How to wait until function returns a value?

I have a react class.
componentDidMount() {
let refresh_token = localStorage.getItem('refresh_token');
let isTokenActive = this.checkIfRefreshTokenWorking(refresh_token);
}
checkIfRefreshTokenWorking = (refresh_token) => {
let trueOrFalse = false;
fetch('http://localhost:8888/refresh_token?refresh_token='+refresh_token)
.then(response => {
response.json()
trueOrFalse = true;
})
.catch(err => {
trueOrFalse = false;
})
.finally(() => {
return trueOrFalse;
})
}
isTokenActive gets undefined. I want to wait until checkIfRefreshTokenWorking returns a value. Do I need to create a promise for this?
Javascript Promises
The function passed to new Promise is called the executor. When new Promise is created, the executor runs automatically. It contains the producing code which should eventually produce the result. In terms of the analogy above: the executor is the “singer”.
Its arguments resolve and reject are callbacks provided by JavaScript itself. Our code is only inside the executor.
This is not tested code
componentDidMount() {
let refresh_token = localStorage.getItem('access_token');
this.checkIfRefreshTokenWorking(refresh_token)
.then((data) => console.log(data))
.catch(error => console.log(error));
}
checkIfRefreshTokenWorking = (refresh_token) => new Promise((resolve, reject) => {
let trueOrFalse = false;
fetch('http://localhost:8888/refresh_token?refresh_token='+refresh_token)
.then(response => {
response.json()
trueOrFalse = true;
resolve(response.json());
})
.catch(err => {
trueOrFalse = false;
reject(err);
})
.finally(() => {
return trueOrFalse;
resolve();
})
})
You have two options here
Using async/await with refresh token call
Make use of conditional rendering way
// You need to use babel present env for this
async componentDidMount() {
const refresh_token = localStorage.getItem('access_token');
let isTokenActive = false;
try {
const response = awit fetch('http://localhost:8888/refresh_token?refresh_token=' + refresh_token);
const data = await response.json();
// console.log(data);
isTokenActive = true;
} catch(err) {
// Log the error
}
}
// Conditional rendering
this.state = {
...,
isLoading: false,
isTokenActive: false
};
componentDidMount() {
const refresh_token = localStorage.getItem('access_token');
this.setState({ isLoading: true });
fetch('http://localhost:8888/refresh_token?refresh_token=' + refresh_token)
.then(...)
.catch(...)
.finally(...); // Set the value for isLoading/isTokenActive with this.setState accordingly
}
render() {
const { isLoading, isTokenActive } = this.state;
!isLoading ? isTokenActive ? <MyComponent /> : <div /> : <div>Loading...</div>;
}
References:
https://www.valentinog.com/blog/await-react
https://reactjs.org/docs/conditional-rendering.html

promise keep return the empty array

constructor() {
super();
this.state = {
detailWallet: [],
user: [],
historia: [],
namaPertama: [],
namaKirim: [],
}
componentDidMount() {
this.getDetailAPI();
this.getDetailPlaceAPI();
this.getHistoria();
}
getNameAPI = () => {
const id = this.props.match.params.id;
return new Promise(resolve => {
axios.get(`/wallet/user/${id}`)
.then((result) => {
resolve(
this.setState({
namaPertama: result.data.data.content[0].user.firstName
})
)
})
});
}
getHistoria = () => {
console.log(this.getNameAPI())
Promise.all([this.getNameAPI()])
.then(([result]) => {
this.setState({
namaKirim: result
})
})
axios.get(`/wallet/type/6?q=`)
.then((result) => {
this.setState({
historia: result.data.data.content
})
})
}
so i make a function that have a method get inside a promise, and when i console.log it in getHistoria, the promise give me an empty array..
can someone tell me what's wrong with my code? i just learning about the promise so i still don't really know about it, thank you..
You are returning the result of setState, I don't think setState is returning anything. Besides, setState is asynchronous, so you won't have the result of the updated state if you loged the state just after.
Just set the state with setState and return some value.
EDIT:
getNameAPI = () => {
const id = this.props.match.params.id;
return new Promise(resolve => {
axios.get(`/wallet/user/${id}`)
.then((result) => {
let value = result.data.data.content[0].user.firstName;
this.setState({ namaPertama: value})
resolve(value)
})
});
}

how to return value from a promise function

I have a function which checks whether a device is online or not. Below is the code.
const ping = require('ping');
export function findDevices(device) {
try {
const hosts = [device];
let result = null;
hosts.forEach((host) => {
ping.promise.probe(host)
.then((res) => {
console.log(res.alive)
result = res.alive;
return {
Status: result
}
});
});
} catch (err) {
logger.error(err, '[ config - findDevices() ]');
console.error(err);
return {
Status: "Failed"
}
}
}
I am calling this function in a redux action like this:
export function start(device) {
return dispatch => {
const status = Connectionstatus.findDevices(device);
return dispatch({
type: actionTypes.CONNECTIONSTATUS,
payload: {
ConnectionStatus: status
}
})
};
}
I am expective the status variable to be either true or false. But i am getting as undefined even though i am returning the value inside then of the promise function. i have tried awaiting this call and still its not working. Any help would be much appreciated. Thanks.
If that's the case you can do like this
const getStatus = async () => {
try {
const hosts = [device];
const promises = [];
hosts.forEach((host) => {
promises.push(ping.promise.probe(host));
});
const result = await Promise.all(promises);
const status = result.map((r) => { return r.alive; });
return status;
} catch (err) {
logger.error(err, '[ config - findDevices() ]');
return { status: 'Failed' };
}
};
Not 100% sure what all the vars are, but have you considered using async/await to simplify things a bit like this?
const getStatus122 = async device => {
return await Promise.all([device].map(ping.promise.probe))
.then(({ alive }) => alive)
.then(Status => ({ Status }))
.catch(error => {
logger.error(error, '[ config - findDevices() ]');
return { Status: 'Failed' };
})
}
More on that here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
With Promises you should inspect the promised result either in when or catch callback functions. With async/await the code may look a bit simpler. Here is the version with explicit promises.
const ping = require('ping');
const Connectionstatus = {
findDevices: (device) => {
return ping.promise.probe(device).then((res) => {
const result = res.alive;
console.log(result);
return {
Status: result,
};
}).catch((err) => {
logger.error(err, '[ config - findDevices() ]');
console.error(err);
return {
Status: "failed"
}
});
}
}
export function start(device) {
return dispatch => {
Connectionstatus.
findDevices(device).
then((status) => {
dispatch({
type: actionTypes.CONNECTIONSTATUS,
payload: {
ConnectionStatus: status
}
})
});
};
}
You may see that error handling moved to the catch callback function while the dispatch is done in the then callback function. And this is the answer to your question.

How to set state from Multiple API call?

I am having this issue with setState in the code.
Trying to do
what I wanted to achieve is to concatenate results from all the API calls into one variable in State.Should I assign everything in the first API (as in second or third API call wrap within the first API .then(function) ?)
or
should i define each api separately
var requestDigAPI = ...
var requestNEWSAPI =...
and call
this.setstate({
this.state.articles.concat(dig,buzzfeed)
})
What is the right approach?
ERRORs
No matter the method react logs error.
If I set the State in other API within the first API returns
error buzzfeed unidentified
or setState outside of both API
error dig , buzzfeed unidentified
componentDidMount() {
this.setState({loading: true})
var apiRequestDig = fetch("api").then(function(response) {
return response.json()
});
var apiRequestNews = fetch("api").then(function(response) {
return response.json()
})
var apiREquestBuzzFeed = fetch(api).then(function(response) {
return response.json()
})
var combinedData = {
"apiRequestDig": {},
"apiRequestNews": {},
"apiREquestBuzzFeed": {}
};
Promise.all([apiRequestDig, apiRequestNews, apiREquestBuzzFeed]).then(function(values) {
combinedData["apiRequestDig"] = values[0];
combinedData["apiRequestNews"] = values[1];
combinedData["apiREquestBuzzFeed"] = values[2];
return combinedData;
});
var dig = apiRequestDig.then(results => {
let dig = results.data.feed.map(article => {
return {
title: article.content.title_alt,
image: article.content.media.images[0].url,
category: article.content.tags[0].name,
count: article.digg_score,
description: article.content.description,
url: article.content.url
}
})
apiREquestBuzzFeed.then(results => {
console.log(results.big_stories[0].title)
let buzzfeed = results.big_stories.map(article => {
return {
title: article.title,
image: article.images.small,
category: article.category,
count: article.impressions,
description: article.description,
url: "https://www.buzzfeed.com"+article.canonical_path
}
})
})
this.setState({
articles: this.state.articles.concat(dig),
loading: "none"
})
// console.log(this.state);
})
}
thanks for the advice
You could chain your API calls, but Promise.all() allows you to make concurrent calls, so why not use it?
However, I think your API functions should be defined outside of componentDidMount, for more readability and reusability:
/* Outside of your component */
const apiRequest = url => fetch(url).then(response => response.json())
const apiRequestDig = () => {
return apiRequest("https://dig/api/url").then(results => {
return results.data.feed.map(article => {
return {
title: article.content.title_alt
/* ... */
};
});
});
};
const apiRequestNews = () => {
return apiRequest("https://news/api/url").then(results => {
return results.big_stories.map(article => {
return {
title: article.title
/* ... */
};
});
});
};
const apiRequestBuzzFeed = () => {
return apiRequest("https://buzzfeed/api/url").then(results => {
return results.big_stories.map(article => {
return {
title: article.title
/* ... */
};
});
});
};
/* Inside your component */
componentDidMount() {
this.setState({loading: true});
Promise.all([
apiRequestDig(),
apiRequestNews(),
apiRequestBuzzFeed()
]).then(values => {
return values[0].concat(values[1], values[2]);
}).then(results => {
this.setState({
articles: this.state.articles.concat(results),
loading: "none"
});
}).catch(err => {
console.log('Oops, something went wrong', err);
});
}
How about moving state manipulation code within the resolve callback of Promise.all ?
componentDidMount() {
this.setState({loading: true})
const apiRequestDig = fetch("api").then(response => response.json());
const apiRequestNews = fetch("api").then(response => response.json());
const apiREquestBuzzFeed = fetch("api").then(response => response.json());
Promise.all([
apiRequestDig,
apiRequestNews,
apiREquestBuzzFeed
]).then(([dig, news, feed]) => {
const digs = dig.data.feed.map(article => ({
title: article.content.title_alt,
image: article.content.media.images[0].url,
category: article.content.tags[0].name,
count: article.digg_score,
description: article.content.description,
url: article.content.url
}));
const buzzfeed = feed.big_stories.map(article => ({
title: article.title,
image: article.images.small,
category: article.category,
count: article.impressions,
description: article.description,
url: `https://www.buzzfeed.com${article.canonical_path}`
}));
this.setState({
articles: [...this.state.articles, ...digs],
loading: "none"
});
// return anything you want as wrapped with promise
return {
apiRequestDig: dig,
apiRequestNews: news,
apiREquestBuzzFeed: feed
};
});
.catch(e => {
// catch your error here
})
}
Using Promise.all will be the best approach.But keep in mind about the fail fast behavior of Promise.all where in if one request fails then Promise.all will reject immediately MDN Link. This behavior can be mitigated by catching the error and resolving with empty data.
function makeAPICall(postId) {
return fetch(`https://jsonplaceholder.typicode.com/posts/${postId}`).then(res => res.json());
}
var Posts = React.createClass({
getInitialState: function() {
return {};
},
componentDidMount: function() {
var requestsArray = [makeAPICall(1), makeAPICall(2), makeAPICall(3)];
Promise.all(requestsArray).then(values => {
var postTitles = values.map(post => post.title).join(", ");
this.setState({
data: postTitles
});
}).catch(console.error.bind(console));
},
render: function() {
return <div>{this.state.data}</div>;
}
});
ReactDOM.render(
<Posts/>,
document.getElementById('root')
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Categories

Resources