How to implement async in for loop? - node.js

I have a collection called 'alldetails' which have the details of some collection
{
"name" : "Test1",
"table_name" : "collection1",
"column_name" : "column1"
},
{
"name" : "Test2",
"table_name" : "collection2",
"column_name" : "column2"
},
{
"name" : "Test3",
"table_name" : "collection3",
"column_name" : "column3"
}
I have collection1,collection2 and collection3 which have column1,column2,colum3 respectively
I have to fetch all the name from the 'alldetails' and I have to get the min and max value of other table based on the column name.
So I want the output like below
{name: ["Test1","Test2","Test3"],
date: [{min_date: "2018-12-01", max_date: "2018-12-31", name: "Test1"},
{min_date: "2018-12-01", max_date: "2018-12-31", name: "Test2"},
{min_date: "2018-12-01", max_date: "2018-12-31", name: "Test3"}]
}
I tried the below code because of non blocking its not waiting the response.
alldetails.find({}, { _id: 0 }).then(async function(result) {
let result_data = {};
let resolvedFinalArray = {};
let array = [];
result_data["name"]= [];
result_data["date"] = [];
resolvedFinalArray = await Promise.all(result.map(async value => {
result_data["name"].push(value.name)
getResult(value.table_name,value.column_name,function(response){
result_data["date"].push({min_date: response.minvalue, max_date: response.maxvalue, name:value.name})
});
}));
setTimeout(function()
{
console.log(resolvedFinalArray);
}, 3000);
});
Please suggest me a solution.

If you want to wait for getResult then you need to return Promise from result.map callback.
You are not pushing anything to resolvedFinalArray so why bother with console.log(resolvedFinalArray)
alldetails.find({}, {_id: 0}).then(async (result) => {
let result_data = {};
result_data["name"] = [];
result_data["date"] = [];
await Promise.all(result.map(value => {
// create Promise that resolves where getResult callback is fired
return new Promise((resolve) => {
getResult(value.table_name, value.column_name, (response) => {
result_data["name"].push(value.name);
result_data["date"].push({
min_date: response.minvalue,
max_date: response.maxvalue,
name: value.name
});
resolve();
});
});
}));
console.log(result_data);
});
or using for loop
alldetails.find({}, {_id: 0}).then(async (result) => {
let result_data = {};
result_data["name"] = [];
result_data["date"] = [];
for (let i = 0; i < result.length; i++) {
const value = result[i];
await new Promise((resolve) => {
getResult(value.table_name, value.column_name, (response) => {
result_data["name"].push(value.name);
result_data["date"].push({
min_date: response.minvalue,
max_date: response.maxvalue,
name: value.name
});
resolve();
});
});
}
console.log(result_data);
});

use async.eachOfLimit if you want to apply an async function on all element of an array:
var async = require("async");
var array = [{_id: "...."},{...},{...}];
async.eachOfLimit(array, 1, function(element, index, cb){
myAsyncFunctionWithMyElement(element, function(err){
return cb(err);
});
}, function(err){
// final callback
});

The array forEach method won't work with async function (unless you do deeply evil things like redefining the prototype). This question has a nice insight of the internal.
If you don't want to rely on external libraries, an easy (and my favourite) approach is something like:
for (let i = 0; i < <your array>.length; i++ ) {
await Promise.all( <your logic> );
}
Just adapt it to your need! :)

You might want to use the for await of loop. See this blog post for details.
This, IMHO, is the most modern way to do it, and it doesn't require you to load any external dependencies, since it is built-in to the language itself. It's basically very similar to the classical for of loop.

This should work, if all lexical scope are taken to consideration. Async each is also is better option it would reduce if else blocks and manage promise for you.
alldetails.find({}, { _id: 0 })
.exec((err, result) => {
if (!err) {
let resolvedFinalArray = [];
result.map((value) => {
resolvedFinalArray.push({
name: value.name,
date: []
});
getResult(value.table_name, value.column_name, (err, response) => {
if (!err) {
resolvedFinalArray[resolvedFinalArray.indexOf(value.name)]['date'].push({
min_date: response.minvalue,
max_date: response.maxvalue,
name:value.name
});
} else {
// Send your error messsage.
// res.status(500).send(err);
}
});
});
console.log(resolvedFinalArray);
// res.send(resolvedFinalArray);
} else {
// Send your error messsage.
// res.status(500).send(err);
}
});

Related

Remove object array items by comparing object array from mongodb

There is document which is having array of object inside.
Like
Objectid('')
fruits : [{_id:'2'},{_id:'3'},{_id:'4'}]
I want to delete these items fruits_id = [{_id:'3'},{_id:'4'}].
fruit_group.updateOne(
{collection_id: collection_id},
{$pullAll: {"fruits": fruits_id}}
)
so far i found below logic which i think is not efficient.
routes.post('/removeFruits', async (request, response, next) => {
var post_data = request.body;
var collection_id = post_data.collection_id;
var fruits_ids = JSON.parse(post_data.fruits_ids);
var prev_fruits;
await fruit_group.findOne({'collection_id': collection_id}, function (err, result) {
if (err) {
console("Some error occurred");
response.json({'message': "Some error occurred", 'result': 'false'});
}
prev_fruits = result.fruits;
});
for (var i = 0; i < fruits_ids.length; i++) { // this will delete all occurring items from array
var key = fruits_ids[i].user_id;
prev_fruits.filter(x => x.user_id === key).forEach(x => prev_fruits.splice(prev_fruits.indexOf(x), 1));
}
await fruit_group.updateOne({'collection_id': collection_id}, {$set: {'fruits': prev_fruits}}, function (err, result) {
if (err) {
response.json({'message': "Some error occurred", 'result': 'false'});
}
response.json({'message': 'Deletion successfully', 'result': 'true'});
});
});
is there anyway to achieve the same result?
Assuming fruits_id = [{ _id: '3' }, { _id: '4' }], you could do something like this using $pull and $in:
await fruit_group.updateOne({'collection_id': collection_id}, { $pull: { fruits: { $in: fruits_id }}})
This follows the example of removing all items that equal a specified value.

How to return 2 arrays after saving data to mongodb using node js

I need help with code below. I get an array of items from the client then the goal is to save them in mongodb and return the list classified as 'saved' and 'failed' items. sample of failed items are those that are duplicate on a unique attribute.
I know the code below will not work because of variable scope. how do i get around it? the code below returns an empty array for both savedItems and failedItems. Thanks!
router.post('/addItems', async (req, res, next) => {
let items = req.body;
let result = {
savedItems: [],
failedItems: []
};
function saveData() {
for (i = 0; i < items.length; i++) {
item = items[i];
Model.create({ ...item }, (err, data) => {
if (err) {
result.failedItems.push(item);
} else {
result.savedItems.push(item);
}
});
}
return result;
}
saveData().then(result => {
res.send({
results: result
});
});
});
router.post('/addItems', async (req, res, next) => {
// use try catch when use async
try {
let items = req.body;
let result = {
savedItems: [],
failedItems: []
};
for (let i = 0; i < items.length; i++) {
const item = items[i];
// use the returned promise instead of callback for Model.create
const data = await Model.create({ ...item });
result.savedItems.push(item);
// if also need to handle failed item in result use anathor try catch inside
/*try {
const data = await Model.create({ ...item });
result.savedItems.push(item);
} catch( err ) {
result.failedItems.push(item);
}*/
}
res.send({
results: result
});
} catch( err ) {
// To all the errors unexpected errors + thrown rejected promises
res.send({
error: err
});
}
});
Your saveData method didn't return a promise, try this
function saveData() {
return new Promise(resolve => {
let items = req.body;
let result = {
savedItems: [],
failedItems: []
};
let promises = [];
for (i = 0; i < items.length; i++) {
item = items[i];
let promise = new Promise(resolve => {
Model.create({ ...item }, (err, data) => {
if (err) {
result.failedItems.push(item);
} else {
result.savedItems.push(item);
}
resolve();
});
});
promises.push(promise);
}
Promise.all(promises).then(() => resolve(result));
})
}

Nodejs wait for query

I'm using Nodejs with MongoDB(mongoose along with express).
Since I don't trust the user data, I need to verify it from the database.
input data:
{
"id": "someid",
"nottrusteddata": [ {"id": "1"}, {"id" :"2"}]
}
In my function, I'm verifying the data:
router.post("/validate", (req, res,next) =>{
let validated_data = validate_data(req);
console.log(JSON.stringify(validated_data));
const mydata = new Mydata({
id: req.body.id,
lst : validated_data
});
console.log("mydata: " + JSON.stringify(mydata));
/* Some Usefull stuff is here */
res.status(200).json();
}
function validate_data(req){
let validated_data = []
for(let i = 0; i < req.body.nottrusteddata.length; i++)
{
Databaseobject.findOne({'id': req.body.nottrusteddata[i].id})
.exec()
.then(dbobject =>{
if(dbobject) // not undefined, it exists in the database
{
// Some logic with the object returned from the database
let tmp_object = {};
tmpobject.id = dbobject.id;
// Append it to the list, so that the upper function can use it
validated_data.push(tmp_object);
}
})
}
return validated_data;
}
The desired output should contain the correct information coming from the database, however, due to the async nature of the nodejs, validated_data returns null.
I have also tried using Promise. I couldn't succeed it.
const validate_data = function(req){
return new Promise(function(resolve,reject){
let validated_data = []
for(let i = 0; i < req.body.nottrusteddata.length; i++)
{
Databaseobject.findOne({'id': req.body.nottrusteddata[i].id})
.exec()
.then(dbobject =>{
if(dbobject) // not undefined, it exists in the database
{
let tmp_object = {};
tmpobject.id = dbobject.id;
validated_data.push(tmp_object);
}
})
}
resolve(validated_data);
}
}
What am I doing wrong? How can I wait for the database query to finish, then execute the main part? If there is only one validation, I could've used .then(). However, the list might have contained many elements and I need to wait for all of them to be verified.
Your Databaseobject.findOne() calls are asynchronous so your promise will resolve before any of them complete.
You can make use of Promise.all to wait until all of your promises resolve.
Hopefully, this will work for you:
router.post("/validate", (req, res) => {
validate_data(req.body.nottrusteddata)
.then(validated_data => {
const mydata = new Mydata({
id: req.body.id,
lst: validated_data
})
// Some useful stuff is here
res.status(200).json()
})
.catch(err => {
// Handle error
})
}
function validate_data(nottrusteddata) {
// Create array of pending promises
const promises = nottrusteddata
.map(item => {
return Databaseobject
.findOne({ 'id': item.id })
.exec()
})
// Wait for all promises to resolve
return Promise.all(promises)
.then(docs => {
return docs
.filter(dbobject => dbobject) // Filter out undefined
.map(dbobject => {
return { id: dbobject.id }
})
})
}
If you want, you could also use async-await here:
router.post("/validate", async (req, res) => {
try {
const validated_data = await validate_data(req.body.nottrusteddata)
const mydata = new Mydata({
id: req.body.id,
lst: validated_data
})
// Some useful stuff is here
res.status(200).json()
}
catch(err) {
// Handle error
}
})

NodeJS Promise and Async Problems (Firebase)

The problem is with the promises and the async function. "All moved" is supposed to be logged after everything in async.each is done. But nothing is ever logged.
Here is my exports functions:
var courier_id = data.ref.parent.key;
return admin.database().ref("firewall_queue/"+courier_id+"/orders").once('value',function(orders){
//console.log(Object.keys(orders.val()));
async.each(Object.keys(orders.val()), function (order, callback) {
if(order != "none") {
return moveToWaitingFromFirewall(order).then(callback())
}
},
function (err) {
console.log("All moved");
return admin.database().ref("/firewall_queue/"+courier_id+"/orders/").remove().then(()=>{
return pushToPending(courier_id,data.ref.key);
})
});
})
Here is my moveToWaitingFromFirewall function:
function moveToWaitingFromFirewall(order_id){
var order = {};
order.id = order_id;
var promises = [];
promises.push(new Promise((resolve) => {
admin.database().ref("orders/"+order_id+"/zone").once('value').then(function(zone){
order.zone = zone.val();
resolve();
})
}))
promises.push(new Promise((resolve) => {
admin.database().ref("orders/"+order_id+"/time_order_placed").once('value').then(function(time_order_placed){
order.time = time_order_placed.val();
resolve();
})
}))
//grab zone and time first
return Promise.all(promises).then(()=>{
return admin.database().ref(order.zone+"/wait_order_queue/"+order.id).set(order.time);
})
}
JSON Firebase
"c98" : {
"orders" : {
"0333" : 123123,
"0345" : 12,
"0911" : 123,
"none" : "none"
}
Study this a little bit, and maybe apply to your current code.
Imagine admin.database().ref("orders/"+order_id+"/time_order_placed").once('value') is like delay(time)
// let delay = time => new Promise(res=>setTimeout(res,time));
let delay = function(time){
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve();
},time);
});
}
let myPromise = function(order){
return Promise.all([
delay(500),
delay(500),
delay(1000).then(function(){
console.log('Order complete: ',order);
return; // returns undefined, so cb doesn't pass anything to cb(err), but use ()=>cb() to avoid this anyways.
})
]);
}
let orders = [1,2,3];
async.each(orders,function(order,cb){
myPromise(order)
.then(()=>cb())
.catch(err=>cb(err));
},function(err,data){
if(err){
console.log('Err',err);
}else{
console.log('all Finished');
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/async/2.6.0/async.js"></script>

returning Mongoose query result from Async call

I'm working on a problem where I need to query the db for an instance of a Voter, and use that instance to update an Election, returning to the original function whether that update was successful or not. My code currently looks like this:
function addCandidatesToElection(req, res) {
let electionName = req.body.electionName;
let candidates = req.body.candidates;
let addedCandidatesSucessfully = true;
for(let i=0; i<candidates.length; i++) {
addedCandidatesSucessfully = _addCandidateToElection(electionName, candidates[i]);
console.log("added candidates sucessfully:" + addedCandidatesSucessfully);
}
if(addedCandidatesSucessfully) {
res.send("createElection success");
} else {
res.send("createElection fail");
}
}
which calls this function:
function _addCandidateToElection(electionName, candidateName) {
async.parallel(
{
voter: function(callback) {
Voter.findOne({ 'name' : candidateName }, function(err,voter) {
callback(err, voter);
});
}
},
function(e, r) {
if(r.voter === null){
return 'Voter not found';
} else {
Election.findOneAndUpdate(
{'name': electionName },
{$push: { candidates: r.voter }},
{new: true},
function(err, election) {
if(err){ return err; }
return (election) ? true : false;
});
}
}
);
}
I've already tried printing out the Voter instance(r.voter) to check if it exists (it does), and also printing out the election object returned by the mongoose call, which also works. However, I'm getting a null value in the
addedCandidatesSucessfully = _addCandidateToElection(electionName, candidates[i]);
line, regardless of the result of the call. I think it has to do with the mongoose call returning a local value which is never returned to the function that called _addCandidateToElection, but I don't know how I should return that. I've tried putting control flags such as
let foundAndUpdatedElection = false;
on the first line of _addCandidateToElection and updating it inside the Mongoose query's callback, but apparently it doesn't change.
How should I return the result of the query to the addCandidatesToElection function?
You should probably 'promisify' your code to help you better deal with the asynchronous nature of js. Try the following instead of your example:
function findVoter(candidateName) {
return new Promise(function(resolve, reject) {
Voter.findOne({ 'name' : candidateName }, function(err,voter) {
if(error) {
reject(error);
} else {
resolve(voter);
}
});
});
}
function addCandidateToElection(electionName, candidateName) {
return findVoter(candidateName).then(function(voter) {
return new Promise(function(resolve, reject) {
Election.findOneAndUpdate(
{'name': electionName },
{$push: { candidates: voter }},
{new: true},
function(err, election) {
if (err) {
reject(err);
} else {
resolve(!!election);
}
});
});
}
function addCandidatesToElection(req, res) {
let electionName = req.body.electionName;
let candidates = req.body.candidates;
let addedCandidatesSucessfully = true;
let candidatePromiseArray = [];
for(let i=0; i<candidates.length; i++) {
candidatePromiseArray.push(addCandidateToElection(electionName, candidates[i]));
}
Promise.all(candidatePromiseArray)
.then(function(results) {
console.log(results);
res.send('create election success');
})
.catch(function(error) {
console.error(error);
res.send('failed');
});
}
You will also no longer need to use the async library because promises are now native in ES6

Resources