Wait for then() to finish - node.js

I have been having a problem waiting for one part of my code to finish for me to run the other part,
How can part 1 finish before part 2?
var uidArray = [];
// Part 1
admin.auth().listUsers(1000).then(result => {
result.users.forEach(userRecord => {
uidArray.push(userRecord.uid);
console.log("pushing to uid array user " + userRecord.uid);
});
return;
}).catch(error => {
console.log("Error listing users:" + error);
});
uidArray.forEach(uid => {
// Part 2 of the code
});

You can use async-await for better data manipulation. However, forEach is by default asynchronous it can't wait until for each completes you can use below using async-await with try-catch block.
try{
var uidArray = [];
const result = await admin.auth().listUsers(1000);
for(const userRecord of result.users) {
uidArray.push(userRecord.uid);
console.log("pushing to uid array user " + userRecord.uid);
}
uidArray.forEach(uid => {
// Part 2 of the code
});
} catch(err) {
console.log(err);
}

var uidArray = [];
// Part 1
admin.auth().listUsers(1000).then(result => {
result.users.forEach(userRecord => {
uidArray.push(userRecord.uid);
console.log("pushing to uid array user " + userRecord.uid);
});
return;
}).catch(error => {
console.log("Error listing users:" + error);
}).then(()=>{
uidArray.forEach(uid => {
// Part 2 of the code
});
});
The key here is that after you start a promise, you can keep thening off it. Ideally you should also return a promise after each step, but it's not required.
This link has more information
https://javascript.info/promise-chaining

You can use below code using async-await as it is good for data manipulation.i prefer for loop instead using forEach.
async function test(){
try{
let result = await admin.auth().listUsers(1000);
let usersArray = result.users;
var uidArray = usersArray.map(function(item) { return item.uid; });
// here you can loop through uidArrar either for or forEach whatever you want
for(let i =0 ; i< uidArray.length; i++){
// your code to be executed in second part
}
} catch (e) {
//error
}
}
test()

Related

foreach loop in sync function in nodejs

I have code written
function getDetails (req, res) {
const dbQuery = `call spGetSLAReportsDetails('${req.body.domainId}', ${req.body.days},'${req.body.type}','${req.body.app}')`
try {
connectDatabase(dbQuery).then((rows) => {
if (!_.isEmpty(rows.dbData) && !_.isEmpty(rows.dbData[0])) {
const resultList = []
rows.dbData.pop()
var bar = new Promise((resolve, reject) => {
rows.dbData[0].forEach((element, index, array) => {
let query = `select * from YCW.YWFWIC ic where ic.witem=${element.witem} and ic.reqno=${element.reqno};`
connectDatabase(query).then((data) =>{
for (var i = 0; i < data.dbData.length; i++) {
element[data.dbData[i]["cfield"]] = data.dbData[i]["cvalue"]
}
resultList.push(element)
// console.log(resultList)
}).catch((err) => {
console.log(err)
})
if (index === array.length -1) resolve();
});
});
bar.then(() => {
console.log(resultList);
});
res.status(msgCodeJson.ERR004.code).send({
result: resultList })
} else {
console.log("empty array")
res.status(msgCodeJson.ERR004.code).send({
message : "No data found"
})
// httpResponseHandlerError(res, msgCodeJson.ERR001.code, msgCodeJson.ERR001.msg)
}
}).catch(() => {
httpResponseHandlerError(res, msgCodeJson.ERR002.code, msgCodeJson.ERR002.msg)
})
} catch (err) {
httpResponseHandlerError(res, msgCodeJson.ERR009.code, msgCodeJson.ERR009.msg)
}
}
module.exports.getDetails = getDetails
i want data to be fit in resultlist but i get empty list after all operation.
while in foreach loop i am getting proper output.
kindly help in issue.
i tried with async foreach loop but some syntax error is coming.
kindly help
as mentioned in the comment of the code you're using
Best way to wait for .forEach() to complete
This is OK if there is no async processing inside the loop.
yet you have an async function inside your forEach callback, namly this:
connectDatabase(query).then((data) => {
for (var i = 0; i < data.dbData.length; i++) {
element[data.dbData[i]["cfield"]] = data.dbData[i]["cvalue"]
}
resultList.push(element)
}).catch((err) => {
console.log(err)
})
you'll need to resolve the "outer/parent" promise from inside the "inner/child" promise
I suggest using a regular good old for loop and/or checking the count of resolved promises against the rows.dbData[0].length and calling a final code/function once they match

Wait for foreach to complete NodeJS

So, I am creating an API in NodeJS. In one API, I have to call for loop within mongoose query.
How do I wait for the forEach to complete before executing res.send()? I have attached my code below.
router.post("/getResult", function (req, res) {
const lottery_id = req.body.lottery_id;
const prizeQuery = Prize.find({"lotid": lottery_id});
let response = [];
prizeQuery.exec(function (err, prizes) {
console.log(prizes.length);
if (err) return res.send({success: 0, data: err});
else {
prizes.forEach(prize => {
let prizeData = {};
const winnerQuery = Winner.find({"id": prize._id});
winnerQuery.exec(function (err, winners) {
if (err) return res.send({success: 0, data: err});
else {
prizeData = {
prize: prize,
winners: winners
};
response.push(prizeData);
}
});
});
}
});
return res.send({success:1, data: response});
});
In the code above, return is called before forEach is completed.
Because you are running asynchronous code inside the forEach and forEach will not wait until the asynchronous code finish, to do so, you must wrap your async code with a waiting primitive.
Also the code you provided will call send twice in case of failure, because the return inside the forEach will not actually end the enclosing function.
try {
await Promise.all(prizes.map(async (prize) => {
const winners = await Winner.find({"id": prize._id});
response.push({prize, winners});
}))
res.send({success:1, data: response});
} catch (err) {
res.send({success: 0, data: err});
}
#Rami's answer is correct but addition to that you can also use forof for your code to work. forof works synchronously so there will be no need for promise. like
for (let prize of prizes){
let prizeData = {};
try{
const winnerQuery = Winner.find({"id": prize._id});
const winners = await winnerQuery.exec()
prizeData = {
prize: prize,
winners: winners
};
response.push(prizeData);
}catch(err){
console.error(err)
}
}

Waiting for async function in for loop

I need to wait for an async method (a call to my database) for every object in an array. Right now I have a for loop going through the array calling the async method on each object. The async function is successful but I need to wait for every async call to finish before moving on. Doing some research I have found that Promises combined with await or other methods are the solution to my problem but I haven't been able to figure it out. Here is my code I have right now.
Here is my class with the async function
Vacation : class Vacation {
constructor(id, destination, description, attendee_creator_id) {
this.id = id;
this.destination = destination;
this.description = description;
this.attendee_creator_id = attendee_creator_id;
this.creator = undefined;
this.votes = undefined;
}
find_creator(pool){
return new Promise(function (resolve, reject) {
var self = this;
var query = "SELECT * FROM vacation_attendee WHERE id = " + self.attendee_creator_id;
pool.query(query, function(error, result) {
if (error) {
console.log("Error in query for vacation creator " + error);
return reject(error);
}
var creator = new attendee.VacationAttendee(result.rows[0].full_name, result.rows[0].email, result.rows[0].password_hash);
self.creator = creator;
console.log("creator found ----> " + self.creator.full_name + "in " + self.destination);
resolve(true);
});
})
}
here is how i'm calling the async function
function get_all_vacations(callback) {
var sql_vacations_query = "SELECT * FROM vacation";
pool.query(sql_vacations_query, function(error, vacations_query_result) {
if (error) {
console.log("error in vacations query " + error);
callback(error);
}
var all_complete = loop_through_vacations(vacations_query_result.rows, pool);
callback(null, all_complete);
});
}
async function loop_through_vacations(vacations_incomplete, pool) {
var all_vacations = [];
for (var vacation_data of vacations_incomplete) {
var vacation_at_index = new vac.Vacation(vacation_data.id, vacation_data.destination, vacation_data.description, vacation_data.attendee_creator_id);
vacation_at_index.find_creator(pool)
.then(()=> {
all_vacations.push(vacation_at_index);
})
.catch(error=> {
console.log(error);
});
console.log("passed vacation " + vacation_at_index.destination);
}
return await Promise.all(all_vacations);
}
You do it in a wrong way, you don't wait anything in you for-loop.
return await Promise.all(all_vacations); does not work, because all_vacations is not a Promise array.
In your case, we have many way to do this, my way just is a example: Create a array to store all promises what have been created in your for-loop, then wait until the all promises finished by Promise.all syntax.
async function loop_through_vacations(vacations_incomplete, pool) {
var all_vacations = [];
var promises = []; // store all promise
for (var vacation_data of vacations_incomplete) {
var vacation_at_index = new vac.Vacation(vacation_data.id, vacation_data.destination, vacation_data.description, vacation_data.attendee_creator_id);
promises.push( // push your promise to a store - promises
vacation_at_index.find_creator(pool)
.then(() => {
all_vacations.push(vacation_at_index)
})
.catch((err) => {
console.log(err); // Skip error???
})
);
}
await Promise.all(promises); // wait until all promises finished
return all_vacations;
}
I don't know why you use async/await mix with callback style, I recommend async/await for any case:
Mix style:
function get_all_vacations(callback) {
var sql_vacations_query = "SELECT * FROM vacation";
pool.query(sql_vacations_query, async function(error, vacations_query_result) { // async
if (error) {
console.log("error in vacations query " + error);
return callback(error); // return to break process
}
var all_complete = await loop_through_vacations(vacations_query_result.rows, pool); // await
callback(null, all_complete);
});
}
async/await:
async function get_all_vacations() {
var sql_vacations_query = "SELECT * FROM vacation";
var rows = await new Promise((resolve, reject) => {
pool.query(sql_vacations_query, function(error, vacations_query_result) {
if (error) {
console.log("error in vacations query " + error);
return reject(error);
}
resolve(vacations_query_result.rows);
});
})
var all_complete = await loop_through_vacations(rows, pool); // await
return all_complete;
}
For me, you can try to reformat your SQL queries, the code will be cleaner and it will take less time to make a big query once than multiple queries.
If I understand joins in your database:
SELECT *
FROM vacation_attendee AS attendee
INNER JOIN vacation ON vacation.attendee_creator_id = attendee.id
-- WHERECLAUSEYOUWANT
Then:
async function get_all_vacations(callback){
const allComplete = await makeThatBigQuery(); // + treat data as you want
callback(null, allComplete);
}
In your definition of get_all_vacations you don't wait all_complete, so the callback is called before all_complete is ready
This is how the await/promises part of the code should look like:
function get_all_vacations(callback) {
var sql_vacations_query = "SELECT * FROM vacation";
pool.query(sql_vacations_query, async function(error, vacations_query_result) {
if (error) {
console.log("error in vacations query " + error);
callback(error);
}
var all_complete = await loop_through_vacations(vacations_query_result.rows, pool);
callback(null, all_complete);
});
}
async function loop_through_vacations(vacations_incomplete, pool) {
const promises = [];
for (var vacation_data of vacations_incomplete) {
var vacation_at_index = new vac.Vacation(vacation_data.id, vacation_data.destination, vacation_data.description, vacation_data.attendee_creator_id);
promises.push(vacation_at_index.find_creator(pool));
}
return await Promise.all(promises);
}
Ideally you should be removing all the callbacks(not sure if asyn/await is supported for pool.query methods) and change the whole code to async/await style. Also you should be resolving the promise from 'find_creator' with resolve(creator), so that the value is obtained when the promise is resolved.

async save multiple document with mongoose

i am updating 2 documents using mongoose.save(), but i think the way I am doing is is not safe, as far as i know i need to use async to make sure all documents are being executed
// array containing the 2 documents from db
let schedules
let newItem = {
isActive: room.isActive,
name: room.roomname
};
// adding new items to nested array
schedules[0].rooms.push(newItem);
schedules[1].rooms.push(newItem);
// saving / updating documents
var total = schedules.length,
result = [];
function saveAll() {
var doc = schedules.pop();
doc.save(function(err, saved) {
if (err) throw err; //handle error
result.push(saved);
if (--total) saveAll();
else {
// all saved here
res.json(result);
}
});
}
saveAll();
any explanation how to do it correctly
We can use promise.all for this but we need to change your save function to promise based function
...
var total = schedules.length,
result = [];
function saveAll() {
const promises = schedules.map(schedule => save(schedule));
return Promise.all(promises)
.then(responses => {
// all saved processes are finished
res.json(responses);
})
}
// convert callback `save` function to promise based
function save(doc) {
return new Promise((resolve, reject) => {
doc.save((err, saved) => {
if (err) {
reject(err);
}
resolve(saved);
});
});
}
If you can use async/await we can make saveAll function cleaner
async function saveAll() {
const promises = schedules.map(schedule => save(schedule));
const responses = await Promise.all(promises);
res.json(responses);
}
Hope it helps
Use Promises :
doc1.save().exec().then(
data => {
doc1.save().exec().then(
data2 => console.log(data2)
).catch(err2 => console.log(err2))
}
).catch(err1 => console.log(err1))

Promise inside async

I am writing a code using Node.js. I want to parse JSON array, retrieve elements from JSON array, make db call and assign values to JSON array. Make this complete operation in synchronous way. For this I wrote code using for loop:
for (let i = 0; i < items.length; i++) {
if(items[i].type === 'PickSimple'){
operation(item.searchSpec)
.then(lov => {
items[i].listOfValues = lov;
})
.catch(err =>{
console.log(err);
});
}
}
console.log("Final OBJ : "+items)
function operation(lov) {
return new Promise((resolve, reject) => {
Listofvalue.find({type: lov}, function(err, listofvalues) {
if (err) {
return reject(err);
}
return resolve(listofvalues);
});
});
But node is asynchronous, I am not getting desired result. So I have used async:
async.each(items,
function(item,callback) {
if(item.type === 'PickSimple'){
operation(item.searchSpec)
.then(lov => {
item.listOfValues = lov;
}).catch(err =>{
console.log(err);
});
}
}, err => {
if (err) console.error(err.message);
}
);
I have also tried using async.forEachOf.
Still I am not getting desired result. Is anything missing?
EDIT
async function processArr(items){
console.log("Inside processArr "+JSON.stringify(items));
for(const item in items){
console.log("Inside for loop, item : "+item);
if(item.type === 'PickSimple'){
var listOfValues = await operation(item.searchSpec)
item.listOfValues = listOfValues;
}
}
console.log("ProcessArr Final OBJ : "+JSON.stringify(items));
}
Output:
Inside processArr [{"name":"Call Related To","type":"PickSimple","searchSpec":"TM_CALL_RELATED_TO_SERVICE"},{"name":"Disposition Codes","type":"Text","searchSpec":""},{"name":"VOC 1","type":"Text","searchSpec":""}]
Inside for loop, item : 0
Inside for loop, item : 1
Inside for loop, item : 2
If you're running Node 8.x+ you can use the async/await. The following for...in should await for promise to complete before iterating to next item.
PS. I've not tested that method, let me know if it works for you.
async function processArr(items){
for(const item in items){
if(items[item].type === 'PickSimple'){
var listOfValues = await operation(items[item].searchSpec)
items[item].listOfValues = listOfValues;
}
}
console.log("Final OBJ : "+items)
}
EDIT:
You're getting undefined because you're calling console.log inside console.log.

Resources