How i can await the result of the AsyncStorage? - reactjs

i need to run the AsyncStorage first before run the other functions , but AsyncStorage take a time to run and it give the resutlt in the end of the code
constructor(props) {
super(props);
AsyncStorage.getItem(TOKEN).then((r) => {
console.log("AsyncStorage Function")
})
this.state = { token: null }
console.log("The last Function")
}

Ideally you shouldn't be performing any async tasks inside a constructor assuming this is a react class component.
So if you wish to perform any async tasks you could make use of componentDidMount from the component lifecycle method.
But in general if you want to wait for the async operation then this can be achieved in different ways
Using Promises
const promiseFun = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Success")
}, 1000)
})
}
promiseFun().then((res) => {
//here res will be what is returned from the promise function
console.log(res);
//All the required things that needed to be perfomed after the async operation should be placed here.
});
//Whatever the actions/statements written here will be executed before the async operation is finished
console.log("Before Promise Returned");
Using async/await
const promiseFun = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Success")
}, 1000)
})
}
const execFun = async() => {
let res = await promiseFun();
//All the statments from here will be executed only after the promise/async function returned.
console.log(res);
}
execFun();
//All the statements here will be executed without waiting for the promise/async function gets executed
console.log("Before Promise Returned");

Related

React how to wait for all axios to finish

I want to wait until all axios in useEffect are finished.
UseEffect:
useEffect(() => {
async function getHomePageContent() {
await HomePageServices.getSliderContent().then((response) => {
setSliderProduct(response.data);
});
await HomePageServices.getRecommendedProducts().then((response) => {
setRecommendedProducts(response.data);
});
await HomePageServices.getMostOrderProducts().then((response) => {
setMostOrderProducts(response.data);
});
await HomePageServices.getMostRatedProducts().then((response) => {
setMostRatedProducts(response.data);
});
}
getHomePageContent().catch((error) => {
console.log(error)
});
}, []);
Class:
class HomePageServices{
async getSliderContent(){
return await axios.get(baseURL+"/slider")
}
async getMostRatedProducts(){
return await axios.get(baseURL+"/mostRatedProducts")
}
async getMostOrderProducts(){
return await axios.get(baseURL+"/mostOrderProduct")
}
async getRecommendedProducts(){
return await axios.get(baseURL+"/recommendedProduct")
}
}
Can someone explain to me how to wait for all axios to end, and if one failed, how to find out which one it was?
Try using Promise.allSettled() which takes an iterable (e.g. array) of promises and resolves into array of results of each of them.
Results are represented as objects with status key, which can be rejected or fulfilled. The second key of the object is either value containing the resolved value, or reason in case promise was rejected.
Taking this, then your code in useEffect might be something like this:
useEffect(() => {
const getHomePageContent = async () => ({
const promises = [
HomePageServices.getSliderContent(),
HomePageServices.getRecommendedProducts(),
HomePageServices.getMostOrderProducts(),
HomePageServices.getMostRatedProducts()
];
const data = await Promise.allSettled(promises);
const [slider, recommended, mostordered, mostrated] = data;
// result for each of promise
console.log(slider); // { status: 'fulfilled', value: 123 }
console.log(recommended) // { status: 'rejected', reason: 'blah'}
});
getHomePageContent().catch((er) => console.log(er))
}, [])

Correct way to cleanup useEffect with Promise

I have useEffect callback where I'm fetching some data from several API. To do that I'm using Promise and my code looks like that:
useEffect(() => {
const fetchAllData = async () => {
const resourceOne = new Promise((resolve) => {
// fetching data from resource one and changing some states
})
const resourceTwo = new Promise((resolve) => {
// fetching data from resource two and changing some states
})
const resourceThree = new Promise((resolve) => {
// fetching data from resource three and changing some states
})
await Promise.all([resourceOne, resourceTwo, resourceThree])
.then(() => {
// changing some states
})
}
return fetchAllData()
},[])
How I understand in this situation useEffect unmount before fetching all data and then gives me an warning
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
So how to write correctly cleanup code to avoid this warning?
You can use a boolean and toggle it in the cleanup function (the function that is returned in the callback passed to useEffect)
useEffect(() => {
let shouldUpdate = true;
const fetchAllData = async () => {
const resourceOne = new Promise((resolve) => {
// fetching data from resource one and changing some states
})
const resourceTwo = new Promise((resolve) => {
// fetching data from resource two and changing some states
})
const resourceThree = new Promise((resolve) => {
// fetching data from resource three and changing some states
})
await Promise.all([resourceOne, resourceTwo, resourceThree])
.then(() => {
if(shouldUpdate) {
// update state
}
})
}
fetchAllData()
return () => {
shouldUpdate = false;
}
},[])
If the component is unmounted the cleanup function will be called and shouldUpdate will change to false. When the promises resolve, the state will not update as shouldUpdate is no longer true.
You can make use of Abort Signal, to abort all promises,
const controller = new AbortController();
const fetch = new Promise((resolve, reject) => {
// fetch data here
controller.signal.addEventListener('abort', () => reject());
});
controller.abort(); // promise is cancelled
Translating to your use-case:
const controller = new AbortController();
useEffect(() => {
const fetchAllData = async () => {
const resourceOne = new Promise((resolve, reject) => {
// fetching data from resource one and changing some states
controller.signal.addEventListener('abort', () => reject());
})
const resourceTwo = new Promise((resolve, reject) => {
// fetching data from resource two and changing some states
controller.signal.addEventListener('abort', () => reject());
})
const resourceThree = new Promise((resolve, reject) => {
// fetching data from resource three and changing some states
controller.signal.addEventListener('abort', () => reject());
})
await Promise.all([resourceOne, resourceTwo, resourceThree])
.then(() => {
// changing some states
})
}
fetchAllData();
return () => {
controller.abort(); // all promise are cancelled where 'abort' listener is specified
}
},[])
And call controller.abort() to cancel one or more number of promises where you included the event listener when the component unmounts.
When abort() is called, the promise rejects with an AbortError, so you can listen to that and handle aborted rejects differently.

React class component issue in order of execution

I have the following code in my React class component.
For some reason, I am observing that, inside componentDidMount, despite having the keyword await before the call to this.getKeyForNextRequest(), the execution is jumping to the next call, this.loadGrids().
Am I doing something wrong here?
async componentDidMount() {
await this.getKeyForNextRequest();
await this.loadGrids();
}
getKeyForNextRequest = async () => {
const dataRequester = new DataRequester({
dataSource: `${URL}`,
requestType: "POST",
params: {
},
successCallback: response => {
console.log(response);
}
});
dataRequester.requestData();
}
loadGrids = async () => {
await this.loadGrid1ColumnDefs();
this.loadGrid1Data();
await this.loadGrid2ColumnDefs();
this.loadGrid2Data();
}
You can try using the Promise constructor:
getKeyForNextRequest = () => {
return new Promise((resolve, reject) => {
const dataRequester = new DataRequester({
dataSource: `${URL}`,
requestType: "POST",
params: {},
successCallback: response => {
console.log(response);
resolve(response);
}
});
});
}
This ensures you're waiting for a relevant promise, one that resolves only upon successCallback completing, rather than one that resolves instantly to undefined as you have it currently.
This is called "promisifying" the callback.
If DataRequester offers a promise-based mode, use that instead of promisifying the callback.

using axios with promise API

I am using a promise based hook in a React app to fetch async data from an API.
I am also using a Axios, a promise based http client to call the API.
Is it an anti-pattern to use a promise based client inside another promise? The below code does not seem to work.
const getData = () => {
return new Promise((resolve, reject) => {
const url = "/getData";
axios.get(url)
.then(function(response) {
resolve(response);
})
.catch(function(error) {
reject(error);
});
});
const useAsync = (asyncFunction) => {
const [value, setValue] = useState(null);
const execute = useCallback(() => {
setPending(true);
setValue(null);
setError(null);
return asyncFunction()
.then(response => setValue(response))
.catch(error => setError(error))
.finally(() => setPending(false));
}, [asyncFunction]);
useEffect(() => {
execute();
}, [execute]);
return { execute, pending, value, error };
};
};
const RidesList = () => {
const {
pending,
value,
error,
} = useAsync(getData);
Oh man. I think you have a fundamental misunderstanding about how Promises work.
First, axios already returns a Promise by default. So your whole first function of getData can be reduced to:
const getData = () => {
const url = "/getData"
return axios.get(url)
}
But the meat of your code seems to indicate you want a querable Promise - so you can check the status of it for whatever reason. Here's an example of how you would do it, adapted from this snippet:
function statusPromiseMaker(promise) {
if (promise.isResolved) return promise
let status = {
pending: true,
rejected: false,
fulfilled: false
}
let result = promise.then(
resolvedValue => {
status.fulfilled = true
return resolvedValue
},
rejectedError => {
status.rejected = true
throw rejectedError
}
)
.finally(() => {
status.pending = false
})
result.status = () => status
return result
}
In this way, you can then do something like let thing = statusPromiseMaker(getData()) and if you look up thing.status.pending you'll get true or false etc...
I didn't actually run what's above, I may have forgotten a bracket or two, but hopefully this helps.
I have to admit - I haven't seen anything like this ever used in the wild. I am interested in knowing what you're actually trying to accomplish by this.
Axios itself returns a promise but if you want to make a custom class having your custom logic after each API call then you can use interceptors I was having the same requirement and this is how I am returning promises after applying my custom logic on each API call.
Interceptors will get executed separately after and before each request you made so we can simply use them if we want to modify our request or response.
here is my working solution have a look at it.
callApi = (method, endpoint, params) => {
this.apiHandler.interceptors.request.use((config) => {
config.method = method
config.url = config.baseURL + endpoint
config.params = params
return config
})
return new Promise((resolve, reject) => {
this.apiHandler.interceptors.response.use((config) => {
if (config.status == 200) {
resolve(config.data)
} else {
reject(config.status)
}
// return config
}, error => reject(error))
this.apiHandler()
})
}
Below is the code to call this function
helper.callApi("get", "wo/getAllWorkOrders").then(d => {
console.log(d)
})

How to cover Promise.all(...) statement with unit test

I cannot write a test that covers Promise.all() statement within a asynchronous function (loadMessages()) that is ran in setTimeout() block of componentDidMount method.
In componentDidMount there is this.loadMessages() function that is called within setTimeout callback, in order for me to complete my test i need loadMessages() executed.
componentDidMount() {
const { conversationId } = this.state
const POLLING_INTERVAL = 3000
if (conversationId) {
setTimeout(() => this.loadMessages(), 0)
this.timer = setInterval(() => this.loadMessages(), POLLING_INTERVAL)
} else {
this.setState({ loading: false })
}
}
I resolved setTimeout callback with
await new Promise(resolve =>
setTimeout(() => {
resolve()
}, 3000)
)
and that solves a function call, but when start executing a function the report coverage is saying that Promise.all is not covered and function itself looks like:
async loadMessages() {
const { messages, conversationId, errors } = this.state
let messagesWithAuthors
// initial load
if (messages.length === 0) {
try {
let initialMessages = await runtime.dais.communication.auto.getMessagesByConversationId(
conversationId
)
const messageAuthors = await Promise.all(
initialMessages.map(async message =>
//runtime.dais.orgService.auto.getPersonById(message.sender.id)
runtime.dais.organization.auto.getPersonById(message.sender.id)
)
)
messagesWithAuthors = initialMessages.map((message, i) => ({
...message,
author: messageAuthors[i],
}))
this.setState({
messages: messagesWithAuthors,
messageAuthors,
loading: false,
})
} catch (error) {
this.setState({ errors: [...errors, error], hasErrors: true, modalOpen: true })
}
} else {
let updatedMessages = await runtime.dais.communication.auto.getMessagesByConversationId(
conversationId
)
this.checkIfNeedUpdate(updatedMessages)
}
}
is there some way to mock a values that are returned from Promise.all() into messageAuthors variable?
I am testing using #testing-library/react and my test looks like this
it('ensure that we have chat window shown if we have conversation as a prop', async () => {
const queries = render(
<CommWidget runtime={runtime} conversationId="fe3d52fc-ffb3-482a-aedf-79000645ca70" />
)
await new Promise(resolve =>
setTimeout(() => {
resolve()
}, 3000)
)
const commWidget = queries.container.querySelector(
'.ui-comm-widget .ui.segments.comm-widget #chat-window'
)
expect(commWidget).toBeInstanceOf(HTMLDivElement)
})
Please don't put a timeout in your tests that's an anti-pattern. What happens after the promise resolves? Is the page going to change? If so wait for the change to appear. See this article for an introduction on testing async methods.

Resources