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
}
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 am trying to call a function that calls fetch to an API from a React component in a separate file and am not finding the correct solution to get the correct response back.
When I debug, the result returns before the updateAccount function has completed and the final result is never returned to my update function.
Inside the fetch, the API returns the correct response whether it is successful or has validation errors and those results are correctly assigned to result.success and result.errors but the result doesn't get returned from the function so that the caller can make use of those values.
Inside of my React component:
import { updateAccount } from '../services/requests';
...
const update = (account: EditAccountModel) => {
const result = updateAccount(account);
if(result.errors.length > 0) {
// will notify of errors
console.log(result.errors); // is an empty array instead of validation errors
} else {
// will notify of success
console.log(result.success); // is an empty string instead of success message
}
}
...
My request file
export const updateAccount = (account: EditAccountModel | undefined): EditAccountResponseModel => {
const result = new EditAccountResponseModel();
fetch(baseUrl, {
method: 'PUT',
body: JSON.stringify(account),
headers
})
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
result.success = `${account?.name} was updated successfully!`
})
.catch(error => {
if (typeof error.json === "function") {
error.json().then(jsonError => {
result.errors.push(jsonError);
}).catch(genericError => {
result.errors.push(genericError);
});
}
});
return result;
}
The result reassignment happens inside then catch but it won’t be affective in the way you expected. The guaranteed way to return correct result is via a callback() passed to your updateAccount() if you could afford it:
export const updateAccount = (
account: EditAccountModel | undefined,
callback: Function
): EditAccountResponseModel => {
const result = new EditAccountResponseModel();
fetch(baseUrl, {
method: 'PUT',
body: JSON.stringify(account),
headers
})
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
result.success = `${account?.name} was updated successfully!`
callback(result);
})
.catch(error => {
if (typeof error.json === "function") {
error.json().then(jsonError => {
result.errors.push(jsonError);
callback(result);
}).catch(genericError => {
result.errors.push(genericError);
callback(result);
});
}
});
}
And inside your React component:
const update = (account: EditAccountModel) => {
const handleResult = (res) => {
// your result callback code
// ...
};
updateAccount(account, handleResult);
// ...
}
Alternative way that keeps your current structure is to change your current updateAccount() to an async function, then return await fetch().
You need to wait for the response . I'll let read more about how Promise work in JavaScript.
I wouldn't code updateAccount the same way you did, especially where you use the variable result and update it inside the flow of the promise (you really don't need that). You're also using React so you can use the state to store and update the result of the update function. But let's fix your problem first:
export const updateAccount = async (account: EditAccountModel | undefined): EditAccountResponseModel => {
const result = new EditAccountResponseModel();
await fetch(baseUrl, {
method: 'PUT',
body: JSON.stringify(account),
headers
})
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
result.success = `${account?.name} was updated successfully!`
})
.catch(error => {
if (typeof error.json === "function") {
error.json().then(jsonError => {
result.errors.push(jsonError);
}).catch(genericError => {
result.errors.push(genericError);
});
}
});
return result;
}
First make your function updateAccount async then await the result of the promise.
Now the same thing for the function update:
const update = async (account: EditAccountModel) => {
const result = await updateAccount(account);
if(result.errors.length > 0) {
// will notify of errors
} else {
// will notify of success
}
}
I am getting a backend response to get the driver details using Axios and once after getting that I want to get the name, address of the driver. For that, I tried to set the value to a state variable but couldn't be able to assign, and also I have tried assigned the values to an array state variable and to access them later but I was unable to get a result
This is the code snippet:
getDriverInfo = async () => {
var session = sessionStorage.getItem("session");
var apiBaseUrl = "http://localhost:4000/api/";
// var self = this;
let det = [];
var payload = {
nic: session,
};
axios
.post(apiBaseUrl + "driverDetails", payload)
.then(async (response) => {
console.log(response.data.success);
if (response.data.code == 204) {
console.log("Driver Data retrieved successfull");
response.data.success.map((element) => {
det.push(element);
this.state.Ddetails.push(element);
});
console.log(det.length);
console.log(this.state.Ddetails[0].dln);
await this.setState({
fname: this.state.Ddetails[0].fName,
lname: this.state.Ddetails[0].lName,
});
} else {
console.log("Details does not exists");
alert("Details does not exist");
}
})
.catch(function (error) {
console.log(error);
});
console.log(det.length);
this.state.Ddetails.map((item) => {
console.log("Map");
console.log(item.dln);
});
console.log(this.state.Ddetails.dln);
};
Don't update state directly this is an anty pattern.
https://reactjs.org/docs/state-and-lifecycle.html#do-not-modify-state-directly
I have modified your code a little.
getDriverInfo = async () => {
var session = sessionStorage.getItem("session");
var apiBaseUrl = "http://localhost:4000/api/";
// var self = this;
let det = [];
var payload = {
nic: session,
};
axios
.post(apiBaseUrl + "driverDetails", payload)
.then(async (response) => {
console.log(response.data.success);
if (response.data.code == 204) {
console.log("Driver Data retrieved successfull");
response.data.success.map((element) => {
det.push(element);
//Don't use like this ==> this.state.Ddetails.push(element);
});
this.setState({Ddetails:det},()=>{
console.log(det.length);
console.log(this.state.Ddetails[0].dln);
this.setState(prevState=>{
console.log(det.length);
prevState.Ddetails.map((item) => {
console.log("Map");
console.log(item.dln);
});
console.log(prevState.Ddetails.dln);
return{
fname: prevState[0].fName,
lname: prevState[0].lName,
}
});
})
} else {
console.log("Details does not exists");
alert("Details does not exist");
}
})
.catch(function (error) {
console.log(error);
});
};
this.state = {
name: 'mickey',
lastName: 'mouse',
};
const display = () => {
// wont work
console.log(this.state.name, this.state.lastName);
}
function display() {
// will work
console.log(this.state.name, this.state.lastName);
}
As you can see arrow functions wont work with (this)
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)
});
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});
}
};