This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed 2 years ago.
The below JavaScript is returning a strange version of an array. The below forEach logs nothing to console. I'm probably missing something quite fundamental here, but I know for a fact that the array is populated...unless it's not populated until later due to being async and Chrome just evaluates the console.log after the fact?
let damageRollStringArr = []
monster.damage_types.forEach(async (damageTypeName) => {
const damageType = await checkResponseAndReturnValue(await DamageTypesService.findDamageType(damageTypeName.trim()))
if (damageType !== null) {
damageRollStringArr.push(damageType.damage)
}
})
damageRollStringArr.forEach(el => console.log(el))
// Was returning an empty array[] with objects inside?
return damageRollStringArr
Thanks
demageRollStringArr.forEach (2nd foreach) wont wait for monster.demage_types.forEach (1st foreach) to execute although there is async await in the first forEach
To achieve what you want to do, try this,
let damageRollStringArr = []
const promises = monster.damage_types.map(async (damageTypeName) => {
const damageType = await checkResponseAndReturnValue(await DamageTypesService.findDamageType(damageTypeName.trim()))
if (damageType !== null) {
damageRollStringArr.push(damageType.damage)
}
})
await Promise.all(promises)
damageRollStringArr.forEach(el => console.log(el))
return damageRollStringArr
Or, you can use normal for() loop to replace 1st foreach
let damageRollStringArr = []
for(let i = 0; i < monster.demage_types.length; i++) {
const damageType = await checkResponseAndReturnValue(await DamageTypesService.findDamageType(damageTypeName.trim()))
if (damageType !== null) {
damageRollStringArr.push(damageType.damage)
}
}
damageRollStringArr.forEach(el => console.log(el))
return damageRollStringArr
1st solution will run the loop parallel,
2nd solution will run the loop sequential
Related
This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 7 months ago.
I have a situation where I need to read all the files in a directory and all its subdirectories. I wrote a basic function:
Although this is in React-Native, I'm not sure that matters (or maybe there is a react-native solution).
let fileList = [];
const readDir = async (dir) => {
// This line simply reads the contents of a directory, creating an array of strings
// of all the files and directories inside 'dir'.
const items = await FileSystem.readDirectoryAsync(dir);
items.forEach(async (item) => {
const f = await FileSystem.getInfoAsync(item); // Gets basic information about the item
if (f.isDirectory === true) {
readDir(f.uri); // f.uri is the location of the new directory to read
} else {
// runs if the item is a file
console.log("f.uri: ", f.uri);
fileList.push(f.uri); // A global variable
}
})
};
const parentDirectory = "parent_folder";
readDir(parentDirectory);
// Do more stuff here once all files have been read and added to 'fileList'
This seems to partially work as all the files in all the subdirectories are consoled out from inside the else {...} segment.
However, how can I know the loop is complete so I can continue the script and use 'fileList'?
Don't use forEach with async they don't work together and you can't await the loop. Use a standard for .. of loop, which works nicely with async/await
const readDir = async (dir) => {
const items = await FileSystem.readDirectoryAsync(dir);
for (let item of items) {
const f = await FileSystem.getInfoAsync(item);
if (f.isDirectory === true) {
await readDir(f.uri);
} else {
console.log("f.uri: ", f.uri);
fileList.push(f.uri);
}
}
}
And as readDir is async as well, you have of course either to await it also, or use then to continue once it is finished
async function foo() {
const parentDirectory = "parent_folder";
await readDir(parentDirectory);
//do some other stuff once readdir is finished.
}
or
const parentDirectory = "parent_folder";
readDir(parentDirectory).then(_ => {
//do some other stuff once readdir is finished.
}
This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed 1 year ago.
I have an array of IDs, for each ID I want to make an axios.post call to create something in the database. I've done something like this.
const promises = ids.map(async (id) =>
await methodToCallAxiosPost(params, id)
);
await Promise.all(promises);
// do stuff
now I know that Promise.all runs when all of the promises are resolved which is useful to me to update the UI, but I also want to wait for each promise inside, something like this
const data1 = await methodToCallAxiosPost(params, id1)
const data2 = await methodToCallAxiosPost(params, id2)
const data3 = await methodToCallAxiosPost(params, id3)
Because I am getting potential lock conflicts in the database and I sometimes face issues.
I have tried using for await of but it seemed similar to Promise.all or I might be using it incorrectly.
You can't. They aren't promise aware methods. Use a regular for loop instead.
const results = [];
for (let i = 0; i < ids.length; i++) {
const result = await methodToCallAxiosPost(params, ids[i]);
results[i] = result;
}
This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed 1 year ago.
I am trying to use forEach in array of object and based on the following condition send a request and get that response for using in the other call. Here is the code:
products.forEach(product => {
if (
product.type === 'shows' &&
product.isSoldOut &&
product.otherData
) {
delete product.brand.brandId
product.brand.isTop = true
const res = apiService.create(product.brand)
console.log(res)
}
})
When I add await const res = await apiService.create(product.brand) here it shows a warning too. How can I use async await in this scenario or is there other way to overcome this issue?
From await documentation:
"The await operator is used to wait for a Promise. It can only be used inside an async function".
You need to turn your callback in forEach to async:
products.forEach(async (product) => { ... })
You need to mark the callback as async to allow you to use await.
They always have to come in pairs.
e.g.
products.forEach(async (product) => {
if (
product.type === 'shows' &&
product.isSoldOut &&
product.otherData
) {
delete product.brand.brandId
product.brand.isTop = true
const res = apiService.create(product.brand)
console.log(res)
}
})
Other answers correctly point out that you may just need to mark the method as async. But in this situation where you are firing multiple async calls often you want them to all be fired at the same time, then wait for them to all be resolved. This can be accomplished with Promise.all()
await Promise.all(products.map((product) => {
if (
product.type === 'shows' &&
product.isSoldOut &&
product.otherData
) {
delete product.brand.brandId
product.brand.isTop = true
return apiService.create(product.brand)
}
}));
This question already has answers here:
Using async/await with a forEach loop
(33 answers)
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
I am trying to create a filtered array in an array of objects. I am doing so by running case switch in a forEach and deriving a new array attaching that to a new object and pushing that object to an array stored outside of the foreach. but after running the foreach the length of the external array still shows 0 and the rest of the equation relates to the processing of said array. Its two very large blocks of code so i've tried to redact some.
let updatedDrop = []
drop.forEach(async(tollFree) => {
const zipCodes = Object.values(tollFree)[0].split(",");
let updatedList = []
const {
tracking,
mailList,
} = tollFree;
zips = await Zip.find({
"class": { "$in": zipCodes },
});
zips = zips.map(zip => zip.zip4)
switch (zipCodeSuppress) {
case "keepSelect":
(insert case switch)
break;
}
const distinct = (value, index, self) => {
return self.indexOf(value) === index;
};
updatedList = updatedList.flat()
updatedList = updatedList.filter(distinct)
const combinedCost = unitCost + postageCeiling
const dropItem = {
updatedList,
tracking,
}
updatedDrop.push(dropItem)
//console.log(dropItem)
})
console.log(updatedDrop.length)
let advJobs = [];
let cspJobs = [];
let wbJobs = [];
if (updatedDrop.length > 0){ .....
so until i am able to access the updated asynchronous data the rest of the formula is stalled. How do I do this?
The problem you are facing is that the forEach callback doesn't block the main thread, so at the moment you access the array right after the declaration of the forEach, the callback inside didn't finish executing.
Look at this example
const timer = 2000;
const numbers = [1, 2, 3];
const emptyArray = [];
async function getNumberTwo() {
return new Promise((resolve) => {
setTimeout(() => resolve(2), timer);
});
}
async function withForEach() {
numbers.forEach(async (n) => {
const two = await getNumberTwo();
emptyArray.push(n + two);
});
console.log(emptyArray); // the array is empty here.
setTimeout(() => console.log(emptyArray), numbers.length * timer); // if I log the array after all the time has gone, the array has the numbers.
}
withForEach()
but now if you use for of, or even a normal for I would say
// all the declarations from before..
async function withForOf() {
for (const number of numbers) {
const two = await getNumberTwo();
emptyArray.push(number + two);
}
console.log(emptyArray); // now the array has what you expect it to have
}
withForOf()
So, in conclusion, you can use a normal for or a for of to make it work as you need.
This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed 5 years ago.
Currently we are storing short strings as keys.
These keys correspond to long values which are labels.
I am trying to update the corresponding long value for the key.
But the console.log(record) always executes first and then the inner log statement executes which is not is desired. It always sends the unmodified record to the getRadioValues function caller.
I want to return the record after the corresponding key is updated.
export const getRadioValues = (record: IRecordInput) => {
const singleSelectKeys = ['Race', 'DeathWas', 'MannerOfDeath'];
singleSelectKeys.forEach(async key => {
if (record[key]) {
const dropDownOption = await DropDownOptions.find({ where: { id: record[key] }}) as IPDFSelect;
record[key] = dropDownOption.dataValues.Text;
console.log(record[key]);
}
});
console.log(record);
return record;
};
Your forEach is using an async function which means that the loop probably finishes before any of the promises it created do. To fix this, you need to get the result of your promises and wait on them. However, this means the enclosing function must itself be async, which may or may not be acceptable for your actual use case.
export const getRadioValues = async (record: IRecordInput) => {
const singleSelectKeys = ['Race', 'DeathWas', 'MannerOfDeath'];
await Promise.all(singleSelectKeys.map(async key => {
if (record[key]) {
const dropDownOption = await DropDownOptions.find({ where: { id: record[key] }}) as IPDFSelect;
record[key] = dropDownOption.dataValues.Text;
console.log(record[key]);
}
}));
console.log(record);
return record;
};