I have the following two functions:
async function queryData(){
const query= await axios.get('...')
const queryStatus = portNames.map(...);
const dataStatus= await Promise.all(queryStatus);
return dataStatus;
}
export function actionData(){
const data = queryData();
return{
type:cst.RECEIVE_DATA,
payload:data
}
}
queryData() function return after some line code some data in promise...in the second function i put the data in payload for sending my action to reducer.
the problem is that when i'm trying to pass my data from first function in two second, if i output my variable in console.log() inside the second function,it shows:
instead if i try to print my variable inside the first function
i'm able to access my value from promise...what could be the problem that might create promise pending in actionData()?..therfore how can i pass my data value from promise to action in a way to dispatch my action with relative data to reducer?
Asynchronous functions always return promises. If you want to get access to the value they contain, you either need to call .then on the promise, or put your code in an async function and await the promise.
Since you are using redux, there are some additional considerations. By default, redux does everything synchronously. Dispatching an action should synchronously go through the reducers and update the state. To do async things with redux you'll need to add a middleware. There are a few possible async middlewares, but the one recommended by the redux team is redux-thunk.
With redux-thunk in your project, instead of dispatching an action object, you can dispatch a function. That function is empowered to do async things and dispatch actions when it's done. So a possible implementation for your case would be something like this:
function actionData() {
return async function(dispatch) {
const data = await queryData();
dispatch({
type: cst.RECEIVE_DATA,
payload: data
});
}
}
Let me explain to you the flow here.
First we come here
const data = queryData();
In queryData function we have:
async function queryData(){
const query= await axios.get('...') <------------ first breakpoint ----------------
// ......
}
When this async req is hit, the code doesn't stop it's flow, it continues. Next return statement is hit.
return {
type:cst.RECEIVE_DATA,
payload:data // data is undefined at this moment
}
In the meantime the async request's response comes back. We sequentially execute the next 3 statements of queryData() function
const queryStatus = portNames.map(...);
const dataStatus= await Promise.all(queryStatus);
return dataStatus;
And now the data variable gets updated.
To ensure proper flow, you could write something like this:
export async function actionData() {
const data = await queryData();
return {
type:cst.RECEIVE_DATA,
payload:data
}
}
I'd encourage to you read about async/await on MDN.
Related
I have an async function that makes an API call as so:
const getModel = async () => {
const model = await fetch model...
return model;
};
I also have another function that uses the tfjs model to analyze the sentiment of text:
export const analyzeSentiment = async (text) => {
const model = await getModel();
const score = await predict(text, model);
return score;
};
My problem is I'm mapping multiple sets of text to the analyzeSentiment function, which requires getModel to make a fetch call for every set of text. Is there any way to set the return of getModel()
to a variable that will persist allowing it to be only called once and used for every time analyzeSentiment is called? I have also tried declaring model outside of analyzeSentiment and awaiting it inside the function, however that did not work either. If it matters, I'm using React/Node.
You can create a local cache variable that remembers the value from the first time it was requested and then uses that value from then on:
let cachedModel;
export const analyzeSentiment = async (text) => {
if (!cachedModel) {
cachedModel = getModel();
}
return predict(text, await cachedModel);
};
In this implementation cachedModel will contain a promise from the first time that analyzeSentiment() was called. If analyzeSentiment() is called multiple times in sequence, the code will still only call getModel() once - all the successive calls will just await the same promise. This all works whether the promise is pending or already fulfilled.
Or, if you just want to use the same model for a series of analyzeSentiment() calls, but only for that specific series of calls, then get the model once in the caller and pass the same one into a saquence of analyzeSentiment() calls as a function argument.
question is: if a function runs async but does not return a promise itself (returns void), then will "awaiting" it still work. I thought that 'await' only works if the function returns a promise and not void.
So, in my async function in react I am doing something like this
class Form extends Component {
submitForm = async event => {
event.preventDefault();
const isFormValid = await this.validateForm();
if(!isFormValid) {
return;
}
//some logic
}
validateForm = async () => {
let isValid = false;
//some logic
await this.setState(prevState => {});
//some more logic
return isValid; //this is set inside the setState
}
}
Here I am awaiting this.setState(), but this.setState() does not return a promise, although it is a function that runs asynchronously. At first I thought this would not work. But after debugging and trying it out for a few times, it seems to work. The isValid value is returned only when the this.setState completes its execution (thus successfully setting the isValid value internally depending upon the state).
Although my code is working, it has left me kind of confused, as though why this is working. I read somewhere that await waits till the value on the right side is resolved. But I thought that was only applicable if the expression on the right side was a Promise.
I thought a more valid or correct way was as described in this article I found https://medium.com/front-end-weekly/async-await-with-react-lifecycle-methods-802e7760d802 which makes sense since the custom setStateAsync function returns a promise.
For those of you who can't access the article here is the relevant snippet
class AwesomeProject extends Component {
state = {}
setStateAsync(state) {
return new Promise((resolve) => {
this.setState(state, resolve)
});
}
async componentDidMount() {
StatusBar.setNetworkActivityIndicatorVisible(true)
const res = await fetch('https://api.ipify.org?format=json')
const {ip} = await res.json()
await this.setStateAsync({ipAddress: ip})
StatusBar.setNetworkActivityIndicatorVisible(false)
}
}
edit: (title changed from "Using await for functions NOT returing a promise")
edit2: I read async/await implicitly returns promise? question before I posted my question. I do get that assigning a function the async keyword turns it into something that returns a promise. But I didn't know that just using the await keyword also had the same effect. As this is not mentioned anywhere that I looked. As I said, if setState was some function that I had defined, I get that I could turn it into a promise returning function by assigning the async keyword to it. But I didn't know I could do the same by not assigning the async but just using await while calling it.
TLDR
Basically, the reason it works is because it is synchronous.
but this.setState() does not return a promise, although it is a function that runs asynchronously
This is where you are confused. The this.setState() method is not asynchronous. Or rather, it runs the function you pass to it synchronously but will defer processing the result of that function (the new state that function returns) until the next this.render() cycle.
So depending on what you mean this.setState() can be said to be either synchronous or asynchronous:
function statechanger (prevstate) {
return { foo: bar } // this return value will be handled in the future
// in other words, React will process this return
// value asynchronously
}
this.setState(statechanger); // statechanger() will be called synchronously here
React does not support asynchronous setState() even though the result of setState will be processed asynchronously. If you ever need to use setState asynchronously then you need to use regular asynchronous features of javascript instead of anything provided by setState, eg:
something_async((result) => this.setState({ something: result }))
// or
something_async().then((result) => this.setState({ something: result }))
// or
(async () => {
let result = await something_async();
this.setState({ something: result });
})();
I'm working on a React Native with Expo project, right now i'm trying to delete an item from a list inside a local database. Problem is in the action where I send it. Here is my code.
export const eliminatePlace = (placeId) => {
console.log(`Inside the action with placeID ${placeId}`);
return async dispatch => {
console.log('returned');
try {
const dbResult = await deletePlace(placeId);
console.log(dbResult);
dispatch({
type: DELETE_PLACE,
id: placeId
});
} catch (err) {
throw err;
console.log(err);
}
};
}
Somehow the console.log inside the return didn't fire up, my workaraound was this:
export const eliminatePlace = async (placeId, dispatch) => {
try {
console.log(`Trying to eliminate place with ID ${placeId}`);
const dbResult = await deletePlace(placeId);
console.log(dbResult);
dispatch({type: DELETE_PLACE, id: placeId});
} catch (err) {
throw err;
console.log(err);
}
};
Then it did work, but this is not the best practice, any ideas on why the correct way didn't work?
Here is the link to my github repo where you can download the project:
https://github.com/josmontes/rn-places
In case someone needs to see another place of the code please ask, I didn't add anything else so it doesn't bloats the question and because the problem is inside this function.
You shouldn't be calling async functions inside your action creators. You can read more about why here. Instead, you should use async actions. Even though you aren't making an API call, you can still represent your process as request-success-failure. Basically, dispatch a "request" action and as a side effect call your async function. Once it resolves, you dispatch either "success" or "failure" action, depending on the result. You can then put the results from the database in the payload of the "success" action. You can read more about that here.
I believe the the second example you gave works, because that's basically just the "success" action. It only dispatches a regular action once the async function resolves, while in the first example, the action function itself is async, which redux doesn't like.
I had this executeOrder function in my child component. this was directly calling the API to get data from backend in this way
const executeOrder = async (data, actions) => {
value = await getToken(
props.orderTotal
);
return value.ecToken;
};
As you can see, i was using await function in my executeOrder. With this, it would wait for the execution to complete and then provide me a return value.ecToken
Since i am using REACT, i had to make use of action dispatcher and make a API call from my Redux saga file. So i created an action dispatcher and made a API call this way.
const executeOrder = (data, actions) => {
props.initiateIdentity(props.orderTotal); //ACTION DISPATCHER
console.log("2");
return props.ecToken;
};
Now how do i ensure this action dispatcher completes it execution ? Because, i am dispatching the action and making a API call. The response of the API call, i am putting it in a state and passing that state value from main component to child component via mapStateToProps
By the time props.initiateIdentity(props.orderTotal) is getting executed, it goes to next line return props.ecToen and since this value is still not provided back from API call, i get a blank value.
How do i ensure this action dispatcher has finished its task ?? Someone please help me
PS- here is my SAGA
function* initiateIdentity(action: TInitiateIdentPayPal) {
try {
const resp = yield call(
getToken,
orderTotal,
);
console.log("1");
yield put(savePiBlob(resp));
} catch (err) {
} finally {
}
}
In your case, You have to create 3 status constant of a actions and you get the response in function* in saga file. Your saga function should be look like:
function* callApiInSaga(actions) {
try {
const data = yield callApiByAxiosOrFetch({ type: actions.data });
yield put({
type: constants.CALL_API_SUCCESS,
data,
}); `// if your function calling api is executed, and you get response from server, put({}) in saga will have you transfer data from saga to reducer. and you can use data via mapDispatchToProps:`
} catch (error) {
yield put({
type: constants.CALL_API_FAILED,
});
}
}
Sorry, you cannot achieve it that way.
In the world of Redux, do mutation(by dispatching actions) and get the latest state are two sorts of operations and never mixed out. redux will resolve the updated state somehow, but cannot guarantee the exact timeslot.
If you store ecToken in your redux store then you don't need to return its value from executeOrder. Instead when you call savePiBlob(resp) you can update ecToken in your reducer. The parent can access ecToken with mapStateToProps.
Or if you do want to return ecToken from the child component then use componentDidUpdate to check if ecToken is updated and then return ecToken by calling a parent method.
componentDidUpdate(prevProps) {
if (this.props.ecToken !==null && this.props.ecToken !== prevProps.ecToken)
{
//call parent method here
}
}
My if condition might not be accurate. you can add whatever works for you
I want to parse one excel sheet and before parsing I want some data from backend to map it.
So after clicking on Submit button, I want to trigger three actions one by one and store the response inside store. I am using redux-saga for this.
After the three action (api calls), I will call the parsing function and do the parsing and mapping using that response I will be fetching from store.
I have tried dispatching the three actions one by one. But as soon as it reaches the network client i.e axios instance to call api it becomes async and the next line gets executed.
onSubmit = () => {
/* I will set the loader on submit button till the api is called and all parsing of excel sheet is done. */
this.setState({
showLoader: true,
}, () => {
this.props.getData1(); //Will be saving it in store as data1
this.props.getData2(); //Will be saving it in store as data2
this.props.getData3(); //Will be saving it in store as data3
/* After this I want to call the parsing function to parse the excel sheet data and map accordingly */
parseExcelData(sheetData); //sheet data is the excel data
}
So I expected that when I will call the 'parseExcelData' function, the data from store i.e data1, data2,and data3 will be available in that function.
But all the api call happens after the sheet is being parsed.
I have done it using saga generator functions and is working fine. But I want to know how to deal with this situation with redux.
Putting an api call (or any other async operation) into a saga does not make that action synchronous, it is still async. Separately, redux-saga really does not support getting a result from an action -- you trigger a saga with an action, so when the saga completes, it has no way to return a result to the code that originally triggered it. (You can try to work around this by passing a callback along with the action that triggers the saga, and have the saga call the callback, but I wouldn't recommend this approach.)
I would recommend implementing this without redux-saga, using traditional action creators. The action creators would return promises that make the async api calls, and resolve with the result when they're finished. That might look something like this:
// action creator getData1, getData2, getData3
export const getData1 = () => {
return fetch(apiUrl).then(result => {
return result.json();
}).then(resultJson => {
// also fire an action to put it in the store here
// if other parts of your app need the data
return resultJson;
}).catch(err => {
console.error(err);
});
};
// react component
// assumes 1, 2, and 3 cannot be parallelized
// could also be written with .then instead of await
onSubmit = async () => {
this.setState({showLoader: true}, () => {
const result1 = await this.props.getData1();
const result2 = await this.props.getData2(result1);
const result3 = await this.props.getData3(result2);
});
}
You could have the action creators dispatch an action to put the data in the store instead of resolving the promise with the result. But that means you have to pick up the new data via the component's props, which probably means something in componentDidUpdate that checks if the new props are different from the old props, and if so, calls the next data-fetcher. IMO that approach is much more awkward.