code is not waiting for forEach to complete : Async - javascript

I have a function
transformAllUser = async (userList): Promise<any> => {
return new Promise((resolve, reject) => {
const userListArray = [];
userList.forEach((user) => {
// do something like or something else
userListArray.push({obj : user})
})
resolve(userListArray )
})
}
and to get that value:
const val = await transformAllUser(userList);
console.log(val) // its empty.
I am getting an empty array, What am I doing wrong here so that my code is not waiting for forEach to complete

Here is a solution that you could use.
const transformAllUser = async (userList) => userList.map(user => ({obj: user}))
const users = [{name : "1"}, {name : "2"}, {name : "3"}]
(async () => {
const transformedUsers = await transformAllUser(users)
console.log(transformedUsers)
})()

You don't have to use async await for this. If the code inside your for loop is not going to make any asynchronous operation, you wouldn't need to use async await.
Something simple like this should work for you.
const transformAllUser = (userList) => {
const userListArray = [];
userList.forEach((user) => {
userListArray.push({
obj: user
})
})
return userListArray;
}
const userList = [{name: 'john'}, {name: 'doe'}];
const val = transformAllUser(userList);
console.log(val);
If you still want to use async await, then you need to use it the right way. await can exist only inside an async function.
const transformAllUser = async (userList) => {
return new Promise((resolve, reject) => {
const userListArray = [];
userList.forEach((user) => {
userListArray.push({ obj: user })
// if you have any asyncronous actions here, say, API call or promise, you will await it
// await makeAsyncCall()
})
resolve(userListArray);
})
}
const userList = [{
name: 'john'
}, {
name: 'doe'
}];
transformAllUser(userList).then(val => {
console.log(val);
});

Use async node module.
let async = require('async');
function transformAllUser(){
return new Promise((resolve, reject) => {
async.eachOfSeries(userList, (user, callback)=>{
// do something like or something else
callback(null, {obj : user});
}, (err, results)=>{
if(err){
// Handle error
reject(err);
} else {
resolve(results);
}
})
});
}

Related

How to use foreach and promise

I need to get datas with nested foreach, but I can't fill my array.
At the end of this code I would like to have an array (segId) with my datas but it is empty (because of aynschronous).
I read that I had to use Promise.all but I can't beacause my promise are nested
I'm beginner so my code is far from perfect
How can I do that ?
async function getActivities(strava, accessToken)
{
const payload = await strava.athlete.listActivities({'access_token':accessToken, 'after':'1595281514', 'per_page':'10'})
return payload;
}
async function getActivity(strava, accessToken, id)
{
const payload = await strava.activities.get({'access_token':accessToken, 'id':id, 'include_all_efforts':'true'})
return payload;
}
async function getSegment(strava, accessToken, id)
{
const payload = await strava.segments.get({'access_token':accessToken,'id':id})
return payload
}
var tableau = []
var segId = []
const activities = getActivities(strava, accessToken)
activities.then(value => {
value.forEach((element, index) => {
const activity = getActivity(strava, accessToken, element['id'])
activity.then(value => {
value['segment_efforts'].forEach((element, index) => {
const segment = getSegment(strava, accessToken, element['segment']['id'])
segment.then(value => {
segId.push(value['id'])
})
//console.log(segId)
});
});
})
}) console.log(segId)
Regards
PS : Sorry for my english ...
Something like this should work. You need to always return the inner promises to include them in your promise chain. Consider splitting the code into functions to make it more readable.
getActivities(strava, accessToken).then(activities => {
return Promise.all(activities.map(elem => {
return getActivity(strava, accessToken, elem['id']).then(activity => {
return Promise.all(activity['segment_efforts'].map(elem => {
return getSegment(strava, accessToken, elem['segment']['id']).then(segment => {
segId.push(segment['id']);
});
}));
})
}));
})
.then(_ => {
console.log(segId);
});

Async module returning promise [object Promise]

I am trying to export the value with instrument variable. however data is returning as [object Promise] than object. How can I assign module variable with the final result rather than the promise object.
var instruments = {
data: async () => {
return new Promise((resolve, reject) => {
/// Respond after retrieving the data
resolve({result : "...." }
);
}
}
var symbols = async () => {
const res = await instruments.data();
return res;
}
module.exports.instrument = symbols().then((data) => {
console.log('data');
return data;
}).catch((e) => {
console.log('error');
return {}
});
It looks like you want a singleton cache. Here is a basic implementation
cache.js
let data = {}
module.exports = {
getData: () => {
return data
},
setData: newData => {
data = newData
return
},
}
No need for async here. I would separate this code with the code that retrieves data.
fetchData.js
const cache = require('./cache')
const fetchData = () => {} // fetch data code here
fetchData().then(data => {
cache.setData(data)
})
try this
var instruments = {
data: () => {
return new Promise((resolve, reject) => {
/// Respond after retrieving the data
resolve({result : "...." });
}
}
var symbols = async () => {
const res = await instruments.data();
return res;
}
module.exports.instrument = symbols;
then import instrument method to call and then call
const instrument = require("./filepath");
instrument().then((data) => {
console.log('data');
}).catch((e) => {
console.log(e);
});
If your async function instruments.data() called, it'll await return Promise.
just append await at return for your expected result.
var instruments = {
data: async () => {
return await new Promise((resolve, reject) => {
// Respond after retrieving the data
resolve({result : "...." });
}
}
or remove async. it's same as above.
var instruments = {
data: () => {
return new Promise((resolve, reject) => {
// Respond after retrieving the data
resolve({result : "...." });
}
}

ExpressJs wait till MongoDB fetch data and loop through before the output

I'm having trouble in figuring this to work, I have one table from MongoDB (collection) for comments and another collection for Users.
When the page load it looks up the comment collection and selects the relevant comments, and then it searches the user table to find the name of the user who made the comment, the data will be combined and then the response is sent.
However, the output is sent before the data is fetched from the user table and added. How can I fix this, here is my code
var output = []
const Comments = require('comments.js')
const Users = require('users.js')
function delay( ) {
return new Promise(resolve => setTimeout(resolve, 300))
}
async function delayedProcess(item) {
await delay()
Users.findById(item.user, async function(err, result) {
Object.assign(item, {name: result.name})
output.push(item)
})
}
async function processArray(array) {
const promises = array.map(delayedProcess)
await Promise.all(promises)
return res.json({data: output})
}
Comments.find({page_id: id}).sort({post_date:-1}).limit(6).then(function(data) {
processArray(data)
})
You are not returning promise from the delayedProcess function.
There you go :-
const Comments = require('comments.js')
const Users = require('users.js')
const output = []
function delayedProcess(item) {
return new Promise((resolve, reject) => {
Users.findById(item.user, function(err, result) {
if (err) return reject (err);
output.push({ ...item, name: result.name })
return resolve()
})
})
}
async function processArray(array) {
const promises = array.map(async(item) => {
await delayedProcess(item)
})
await Promise.all(promises)
return res.json({ data: output })
}
const data = await Comments.find({ page_id: id }).sort({ post_date: -1 }).limit(6)
processArray(data)
However you will always get the concatenated array. So instead taking it globally, take it as local variable
function delayedProcess(item) {
return new Promise((resolve, reject) => {
Users.findById(item.user, function(err, result) {
if (err) return reject (err);
return resolve({ ...item, name: result.name })
})
})
}
async function processArray(array) {
const output = []
const promises = array.map(async(item) => {
const it = await delayedProcess(item)
output.push(it)
})
await Promise.all(promises)
return res.json({ data: output })
}
const data = await Comments.find({ page_id: id }).sort({ post_date: -1 }).limit(6)
processArray(data)
More simplified :- Since mongodb queries itself returns promise you do not need to use new Promise syntax.
async function processArray() {
const array = await Comments.find({ page_id: id }).sort({ post_date: -1 }).limit(6)
const output = []
const promises = array.map(async(item) => {
const it = await Users.findById(item.user).lean()
item.name = it.name
output.push(item)
})
await Promise.all(promises)
return res.json({ data: output })
}

React Native .then seems to be running out of order

I currently have the following method in a react native class, however I think this would apply to JS in general but I might be wrong:
dbGetTemplateOptions = () => {
let dataArray = [];
let subcategories = this.state.subcategories;
subcategories.forEach(item => {
let getSubValues = new Promise(function(resolve, reject) {
resolve(item);
})
getSubValues.then((item) => this.dbGetValues(item.subCatId))
getSubValues.then((value) => console.log(2))
});
}
According to my limited js knowledge of promises, in the above I'm resolving a promise and running getSubValues.then() which would mean that each of the .then methods run AFTER the method returns.
In the above code I call the method dbGetValues(item.subCatId)
Which is this:
async dbGetValues(subCatId) {
let subValues = [];
let result = await db.transaction(tx => {
tx.executeSql(
'SELECT * FROM dr_template_relational '
+ ' INNER JOIN dr_report_categorie_values on dr_report_categorie_values.id = dr_template_relational.value_id'
+ ' WHERE dr_template_relational.subcategory_id = ' + subCatId + ' AND dr_template_relational.template_id = ' + this.state.currentTemplateId,
[],
(trans, result) => {
const sqLiteResults = result.rows._array;
sqLiteResults.forEach(el => {
subValues.push({ subCategoryId: subCatId, values: el.value_id, name: el.name, narrative: el.narrative });
})
});
},
(err) => console.error(err),
() => {
console.log(1);
return subValues;
}
);
return result;
}
Notice my console.log(2) is after the then which is calling the method.
Inside the method also notice I have console.log(1). I would expect these to run in order since I'm waiting for it to finish before the next then runs. I realize I'm incorrect because the console.log is actually.
2
2
1
1
dbGetTemplateOptions = () => {
let dataArray = [];
let subcategories = this.state.subcategories;
subcategories.forEach(item => {
let getSubValues = new Promise(function(resolve, reject) {
resolve(item);
})
getSubValues.then((item) => this.dbGetValues(item.subCatId))
getSubValues.then((value) => console.log(2))
});
}
You're resolving the promise before actually calling the asynchronous dbGetValues function. This is why the then triggers before the callbacks of dbGetValues do.
It's hard to know what changes to make without more context of what you're tying to do, but I think you might actually want something like:
dbGetTemplateOptions = () => {
let dataArray = [];
let subcategories = this.state.subcategories;
subcategories.forEach(item => {
let getSubValues = new Promise(async (resolve, reject) => {
const result = await this.dbGetValues(item.subCatId)
resolve(result);
})
getSubValues.then((value) => console.log(2))
});
}
Or to try to simplify even more:
dbGetTemplateOptions = () => {
let dataArray = [];
let subcategories = this.state.subcategories;
subcategories.forEach(async (item) => {
const result = await this.dbGetValues(item.subCatId)
// Do something with result here
console.log(2)
});
}
Obviously this is based on assumptions of what you're doing

Run a function after a loop

I would like to run a function after the for loop done looping, but in my case the function after the for loop runs before the loop even finished.
Here's my code
let orderedItems = [];
for (let i = 0; i < orderCode.length; i++) {
menuModel.findOne({
_id: orderCode[i]
}, (err, order) => {
if (order) {
orderedItems.push(order.name);
}
});
}
console.log(orderedItems); // all the tasks below run before the loop finished looping
let orderData = new orderModel();
orderData._id = helpers.createRandomString(5).toUpperCase();
orderData.username = username;
orderData.orderCode = orderCode;
orderData.orderedItems = orderedItems;
orderData.totalPrice = 5;
orderData.save((err) => {
if (err) {
console.log(err);
callback(500, {
'Error': '1'
});
}
callback(200, {
'Message': 'Successfully ordered'
});
});
as #RishikeshDhokare said everything is executed asynchronously. so the trick is to split the tasks into separate functions.
so first we execute an async function you can use promises or async await
then we say after all the async tasks have been completed do the save task.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
const orderCode = [1, 2, 3]
const menuModel = {
findOne: item => new Promise(resolve => resolve({
_id: item._id,
name: 'name'
}))
}
class orderModel {
save(cb) {
return cb(null)
}
}
/*
ignore all above here i'm mocking your funcs
and vars so that the code works
*/
let orderedItems = [];
function getAllOrderedItems() {
const promises = orderCode.map(id => {
return menuModel.findOne({
_id: id
});
})
console.log('...start the request')
// fire all async items then resolve them all at once
return Promise.all(promises)
.then((data) => {
console.log('...finished all requests')
return orderedItems.concat(data);
})
.catch(err => console.log(err))
}
function handleSave(data) {
let orderData = new orderModel();
console.log(data)
console.log('...start save')
orderData.save((err) => {
console.log('...save finished')
});
}
//do all the async tasks then do the save task
getAllOrderedItems()
.then((data) => handleSave(data))

Categories