My below effect is trying to accomplish the following:
I need to make 3 concurrent XHR requests for jobs, postings, and sale items.
When the data returns, I need to sort the records into open, closed, and 'therest' arrays which I then append to a 'running_total' array.
Once all of the XHR requests have been completed and sorted, I want set the running_total array to state.
Unfortunately, my below code does not seem to respect async / await and once the effect has finished running, I end up with an empty array. Any ideas what I might be doing wrong?
useEffect(() => {
const types = ["jobs", "postings", "sale_items"];
async function getEntityReferencesAsync() {
let running_total = [];
types.forEach(
function(type) {
let open = [];
let closed = [];
let therest = [];
const request = await get_data(
`https://myapi.com.com/${type}/${props.id}`
);
response.then(
function(result) {
const result_array = result.data.records;
result_array.forEach(
function(item) {
item["type"] = type;
if (item.status === "open") {
open.push(item);
} else if (item.status === "closed") {
closed.push(item);
} else {
therest.push(item);
}
}.bind(this)
);
running_total = [
...running_total,
...open,
...closed,
...therest
];
}.bind(this)
);
}.bind(this)
);
return running_total;
}
async function getSortedData() {
const sorted_array = await getEntityReferencesAsync();
setEntityReferencesData(sorted_array);
}
getSortedData();
}, [props.id]);
FYI my get_data function looks like:
async function get_data (endpoint, params) {
if (params === null) {
return await axios.get(endpoint)
} else{
return await axios.get(endpoint, {params: params});
}
};
Related
This code is designed to recursively iterate through a selected directory using the file system access api , and sent it via axios post request.
I have an encountered 2 problems :
I am struggling to find the right way to to enter subdirectories (now it only works for a directory with only files and no subDirs)
as you can see , i set a timeout before the post request , for some reason I cant identify , the promise in savedOCS function , probably resolves before it should.
I have the following code :
async uploadDirectory () {
const directoryHandle = await window.showDirectoryPicker();
const t0 = performance.now();
console.log(`t0 ${t0}`);
this.setState({chosenDirectory: directoryHandle.name} , ()=> {
console.log(this.state.chosenDirectory);
});
await this.handleDirectoryEntry(directoryHandle);
const t4 = performance.now();
console.log(`t4 ${t4}`);
setTimeout(() => {
this.axios.post(
'/DirectoryTransfer', {
directory: this.state.chosenDirectory,
directoryFiles: this.state.directoryFiles
}
).then((resData) => {
// this.fetchRemoteFileSystem();
const t5 = performance.now();
console.log(`t5 ${t5}`);
}).catch((error) => {
console.log(error)
})
}, 5000);
}
async handleDirectoryEntry(directoryHandle,subdir) {
let fileEntries = [];
console.log(directoryHandle);
for await (const entry of directoryHandle.values()) {
console.log(entry);
if(entry.kind === "file" && subdir === '') {
fileEntries.push(await entry.getFile());
}
}
console.log(fileEntries);
await this.saveDocs(fileEntries);
}
saveDocs = async (files) => {
const filePromises = files.map((file) => {
return new Promise ((resolve ,reject)=> {
const fileReader = new FileReader();
fileReader.onload = async ()=> {
try {
let directoryFiles = this.state.directoryFiles || [];
console.log('before setState: ', directoryFiles)
let response = false;
const t2 = performance.now();
console.log(`t2 ${t2}`)
await this.setState(
(prevState) => ({
directoryFiles: [...prevState.directoryFiles, { name: file.name, data: fileReader.result }]
}),
() => {
response = true;
console.log("after setState: ", this.state.directoryFiles);
const t3 = performance.now();
console.log(`t3 ${t3}`)
}
);
resolve(response);
} catch(err) {
reject(err)
}
}
fileReader.onerror = (err) => {
reject(err);
}
fileReader.readAsText(file);
})
})
const fileInfos = await Promise.all(filePromises);
return fileInfos;
}
i tried to find a way to iterate subDirs using recursion , and to async read files inside a directory.
current results :
the code reads the files fine.
without the timeout it sends a post request with an empty array of files.
doesn't have an implementation for subDirs.
I'm trying to build a simple app that fetches data from an API and shows them. I have a scenario in which I have to fetch the IDs of some items, and then for every ID make an API call to get the details. I want to set the array of fetched details as a state, and I can actually do it, but the view does not update and I don't understand why... I guess I'm doing a mess with asynchronous calls, as always...
updateSearchResults() is a state setter passed as a prop from the upper level component, and the holder of the state is that same upper level component.
async function handleSubmit(event) {
event.preventDefault();
if(name) {
let res = await axios.get(`https://www.foo.bar/search`);
if(res.data.items !== null) {
const filteredItems = filterItems(res.data.items);
updateSearchResults(filteredItems); //this works as expected
}
} else {
let res = await axios.get(`https://www.foo.bar/anothersearch`);
if(res.data.items !== null) {
let items= [];
res.data.items.forEach(async item => {
const resDetails = await axios.get(`https://www.foo.bar/getdetails`);
items.push(resDetails.data.items[0]);
})
console.log(items); //this prints the expected result
updateSearchResults(items); //this updates the state but not the view
}
}
}
...
const items= await Promise.all(res.data.items.map(async item => {
const resDetails = await axios.get(`https://www.foo.bar/getdetails`);
return resDetails.data.items[0];
}));
console.log(items); //this prints the expected result
updateSearchResults(items);
You can modify your code to something like this:
async function handleSubmit(event) {
event.preventDefault();
if(name) {
let res = await axios.get(`https://www.foo.bar/search`);
if(res.data.items !== null) {
const filteredItems = filterItems(res.data.items);
updateSearchResults(filteredItems); //this works as expected
}
} else {
let res = await axios.get(`https://www.foo.bar/anothersearch`);
if(res.data.items !== null) {
let items= [];
for await (const item of res.data.items) {
const resDetails = await axios.get(`https://www.foo.bar/getdetails`);
items.push(resDetails.data.items[0]);
}
console.log(items); //this prints the expected result
updateSearchResults(items); //this updates the state but not the view
}
}
}
var value = await this.upload();
if (value == true) {
this.item.photos = this.uploaded;
this.item.price = null;
this.item.qty = null;
this.dataSrv.addItem(this.item)
.then(() => {
this.dataSrv.stopLoading();
this.dataSrv.toast('Item Added Successfully');
this.item = {} as Item;
this.pictures = ['', '', '', ''];
this.uploaded = [];
this.photos = ['', '', '', ''];
})
.catch(err => {
this.dataSrv.stopLoading();
this.dataSrv.toast('Error While Adding Item. Try Again Later');
console.log(err);
});
}
In the above code I am calling upload function to upload picture to Google storage and I am waiting so I can add the image links to uploaded array which will save it to this.item.photos but It does not wait and it directly execute the true condition.
async upload(image?) {
var counter = 0;
this.pictures.forEach(i => {
if (i != '') {
let temp: string = (i) as string;
temp = temp.replace("data:image/jpg;base64, ", '');
temp = temp.replace("data:image/jpg;base64,", '');
temp = temp.replace('must use [property]=binding:', '');
temp = temp.replace('SafeValue', '');
temp = temp.replace('(see https://g.co/ng/security#xss)', '');
const randomId = Math.random().toString(36) + Math.random().toString(36) + Math.random().toString(36) + Math.random().toString(36);
const uploadTask = this.storage.ref('items/').child(randomId).putString(temp, 'base64', {
contentType: 'image/png'
});
uploadTask
.then(response => {
console.log("uploaded");
console.log(response);
this.storage.ref('items/' + randomId).getDownloadURL().subscribe(url => {
this.uploaded.push(url);
counter++;
});
})
.catch(err => {
console.log('not uploaded');
});
}
});
if (counter != this.uploaded.length) {
return false;
} else {
return true;
}
}
I have tried to do return type of Promise in the upload function but nothing changed
I have also tried to do the then after the function call but the same issue happen
Note that pictures array contains base64 image
async functions will not block by themselves. The issue is that the forEach is called synchronously, but it contains code which will run asynchrnously.
A common pattern for doing this:
async upload(image) {
// use .map instead of .forEach
// from the callback function we return a Promise, so the result would be
// an array of promises
const promises = this.pictures.map(i => {
// your logic here
// you must return the promise
return uploadTask
.then(response => { /* ... */ })
.catch(() => { /* ... */ });
})
// Wait for all promises to be resolved.
// Note for the future: Promise.all will reject if at least 1 promise rejects.
// Your code seems to handle this case and converts a failed Promise into a success inside of `.catch` above.
await Promise.all(promises);
// here, the counter should be correct
}
I'm calling an async function (getData()) in componentDidMount, and I'm trying to use this.setState with result of that function.
componentDidMount() {
let newData = getData();
newPodData.then(function (result) {
console.log('result', result)
this.setState({result})
})
}
However, I'm having issues getting my state to properly update. Some additional context - I'm trying to set my initial state with data I am receiving from a database. Is my current approach correct? What's the best way to accomplish this? Here's my async function for more context:
const getTeamData = async () => {
const getTeamMembers = async () => {
let res = await teamMemberService.getTeamMembers().then(token => { return token });
return res;
}
const getActiveTeams = async () => {
let res = await teamService.getActiveTeams().then(token => { return token });
return res;
}
const teamMemberResult = await getTeamMembers()
const activeTeamsResult = await getActiveTeams();
// get team member data and add to teamMember object
let teamMemberData = teamMemberResult.reduce((acc, curr) => {
acc.teamMembers[curr.id] = curr;
return acc;
}, {
teamMembers: {}
});
// get team ids and add to teamOrder array
let activeTeamsData = activeTeamsResult.map(team => team.id)
let key = 'teamOrder'
let obj = []
obj[key] = activeTeamsData;
const newObject = Object.assign(teamMemberData, obj)
return newObject;
}
export default getTeamData;
Changing the function inside the then handler to an arrow function should fix it. e.g:
componentDidMount() {
let newData = getData();
newPodData.then((result) => {
console.log('result', result)
this.setState({result})
})
}
But I'll like to suggest a better way to write that.
async componentDidMount() {
let result = await getData();
this.setState({result})
}
i have a asynchronous function inside that am using forEach and promise.all. my question is how can i make the function stops until one of the value in forEach is rendered completely and then go for the next one.
sentingMailtoOneRestauarnt this is a large function and now am getting only partial values from this because next value in forEach is invoked before the completion of first one.
exports.handler = async () => {
return fetchAllConnectedAcccounts(null).then((response) => {
var promises_array = []
response.forEach((data) => {
if (data) {
var promise = sentingMailtoOneRestauarnt(data, yesterday).then((resp)=>{
promises_array.push(promise);
})
}
})
return Promise.all(promises_array).then((result) => {
return result;
})
}).catch((err) => {
console.log(err)
});
}
From the code, looks like you already are using async here. Skip ove promises -
const responses = await fetchAllConnectedAcccounts(null);
const results = [];
for (const response of responses){
if (response){
results.push(await sentingMailtoOneRestauarnt(response, yesterday));
}
}
// do whatever with results...
Currently your array is full of resolved promises (you push to it only after promise is resolved).
exports.handler = async () => {
return fetchAllConnectedAcccounts(null).then((response) => {
var promises_array = []
response.forEach((data) => {
if (data) {
var promise = sentingMailtoOneRestauarnt(data, yesterday);
promises_array.push(promise); // push to array before promise is resolved
}
})
return Promise.all(promises_array); // wait for resolving here
/*.then((result) => {
return result;
})*/ // unnecessary
}).catch((err) => {
console.log(err)
});