How to simplify this code - node.js

Here I am trying to create a new train and adding seat available information for next 5 days to that train. Here I am doing things repetitively. I wanted to know how to simplify this code.
here I have used express, mongoose and moment.js to generate date.
app.post("/admin",[
hasAccess('admin'),
function(req,res,next){
var today = moment();
Train.create(req.body.train,function(err,train){
if(err){
console.log(err);
}else{
Availability.create({date:moment(today).add(0, 'days').format("DD-MM-YYYY"),available:10},function(err,avail){
if(err){
console.log(err);
}else{
train.availabilities.push(avail);
Availability.create({date:moment(today).add(1, 'days').format("DD-MM-YYYY"),available:10},function(err,avail){
if(err){
console.log(err);
}else{
train.availabilities.push(avail);
Availability.create({date:moment(today).add(2, 'days').format("DD-MM-YYYY"),available:10},function(err,avail){
if(err){
console.log(err);
}else{
train.availabilities.push(avail);
Availability.create({date:moment(today).add(3, 'days').format("DD-MM-YYYY"),available:10},function(err,avail){
if(err){
console.log(err);
}else{
train.availabilities.push(avail);
Availability.create({date:moment(today).add(4, 'days').format("DD-MM-YYYY"),available:10},function(err,avail){
if(err){
console.log(err);
}else{
train.availabilities.push(avail);
train.save();
res.redirect("/trains/"+train._id);
}
})
}
})
}
})
}
})
}
})
}
});
}
]);

1 - Using promises
app.post("/admin", [
hasAccess('admin'),
function (req, res, next) {
var today = moment();
const days = [0, 1, 2, 3, 4];
let createdTrain;
Train.create(req.body.train)
.then(train => {
createdTrain = train;
return Promise.all(days.map(day => {
return Availability.create({
date: moment(today).add(day, 'days').format("DD-MM-YYYY"),
available: 10
});
}));
})
.then(result => {
createdTrain.availabilities = result;
createdTrain.save();
})
.then(saved => {
res.redirect("/trains/" + saved._id);
})
.catch(err => {
console.log(err);
});
}
]);
2 - Using async await [node 7.6 +]
app.post("/admin", [
hasAccess("admin"),
async (req, res, next) => {
try {
const today = moment();
const DAYS = [0, 1, 2, 3, 4];
const train = await Train.create(req.body.train);
const result = await Promise.all(
DAYS.map(day => {
return Availability.create({
date: moment(today)
.add(day, "days")
.format("DD-MM-YYYY"),
available: 10
});
})
);
train.availabilities = result;
const saved = await train.save();
res.redirect("/trains/" + saved._id);
} catch (err) {
console.log(err);
}
}
]);

You can also use async package for creating availability object in parallel by using async.parallel, this make your execution fast and create one function that can create availability object.
app.post("/admin", [
hasAccess('admin'),
function (req, res, next) {
var today = moment();
Train.create(req.body.train, function (err, train) {
if (err) {
console.log(err);
} else {
var functionArr = [];
for (var i = 0; i < 5; i++) {
var availFunction = function (i, callback) {
availability(moment(today), 0, 10, function (err, avail) {
if (err) {
return callback(err);
}
return callback(null, avail);
});
}
functionArr.push(availFunction);
}
async.parallel([
functionArr
], function (err, results) {
if (err) {
console.log(err);
}
train.availabilities.push(results);
train.save();
res.redirect("/trains/" + train._id);
});
}
});
}
]);
function availability(date, days, seats, callback) {
Availability.create({
date: moment(date).add(days, 'days').format("DD-MM-YYYY"),
available: seats
}, function (err, avail) {
if (err) {
return callback(err);
} else {
return callback(null, avail);
}
});
}

Related

How to get data from multiple model in single controller in NodeJS?

I am working on nodejs and i am using "Express js" framework, I am working on Controller and i am trying to get data from "two model functions" but i am getting message "anonymous", How can i get data from multiple "model functions", Here is my code,
This is my homeController
homeController.index = function (req, res, next) {
// fetching data into 'data variable' from FetchRecords model
homeModel.FetchRecords(function (err, data) {
if (err) {
req.flash('error', 'There was no records.');
} else {
res.render('home/index', { data: data });
}
});
homeModel.getAverage(function (average) {
console.log(average);
// res.render('home/index',{data:average});
});
};
This is my homeMOdel
homeModel.FetchRecords = function (data) {
sql.query('SELECT * FROM rating', function (err, res) {
if (err) {
return data(err, null);
} else {
return data(null, res);
}
});
};
homeModel.getAverage = function (average) {
console.log(average);
sql.query('SELECT avg(value) FROM rating', function (err, res) {
if (err) {
return average(err, null);
} else {
return average(null, res);
}
});
};
Inside homeModel just create 1 function instead of 2 separate. You can combine both MySQL queries into one like this.
const FetchRecordsAndAverage = function (data) {
sql.query('SELECT * FROM rating; SELECT avg(value) FROM rating', function (err, res) {
if (err) {
return data(err, null);
} else {
return data(null, res);
}
});
};
module.exports = {
FetchRecordsAndAverage
}
With this you will get combined data of both queries as arrays inside array.
Result of queries can be accessed as data[0] & data[1].
You should export the function from the home model:
const FetchRecords = function (data) {
sql.query('SELECT * FROM rating', function (err, res) {
if (err) {
return data(err, null);
} else {
return data(null, res);
}
});
};
const getAverage = function (average) {
console.log(average);
sql.query('SELECT avg(value) FROM rating', function (err, res) {
if (err) {
return average(err, null);
} else {
return average(null, res);
}
});
};
module.exports = {
FetchRecords,
getAverage
}
And retrieve them in your application by calling:
const { FetchRecords, getAverage } = require('./path/to/home_model');

Node Express pass function results outside of forEach

I am attempting to pass results from a forEach function (that needs to be async, but that's a different story) to ejs. Any help is greatly appreciated
router.get('/', (req, res) => {
Block.find({}).populate('servers').exec((err, foundBlocks) => {
if(err){
req.flash('error', 'Block not found. Please try again.');
res.redirect('back');
} else {
MyDB.find({}, (err, foundDB) => {
if(err){
console.log(err)
} else {
foundDB.forEach((DB) => {
pinger(DB.object, (output) => {
if(output){
const string = output;
DB.status = true
console.log(output)
} else {
DB.status = false;
}
})
});
res.render('settings/index', {
blocks: foundBlocks,
});
}
});
}
});
async/await does not works with forEach loop because forEach does not wait for the response to finish, using normal for loop is the key,
your code after replacing forEach with for loop should be like this
router.get('/', (req, res) => {
Block.find({}).populate('servers').exec((err, foundBlocks) => {
if(err){
req.flash('error', 'Block not found. Please try again.');
res.redirect('back');
} else {
MyDB.find({}, (err, foundDB) => {
if(err){
console.log(err)
} else {
for(let DB of foundDB){
pinger(DB.object, (output) => {
if(output){
const string = output;
DB.status = true
console.log(output)
} else {
DB.status = false;
}
})
});
res.render('settings/index', {
blocks: foundBlocks,
});
}
});
}
});

Async loop didn't wait

I am using node-async-loop for asyncronous programming
var array = ['item0', 'item1', 'item2'];
asyncLoop(array, function (item, next)
{
do.some.action(item, function (err)
{
if (err)
{
next(err);
return;
}
next();
});
}, function (err)
{
if (err)
{
console.error('Error: ' + err.message);
return;
}
console.log('Finished!');
});
Like this I am using three async loops one under one.
I want to send the response only after the third inner loop ends. How can I do so?.
Here is the link for node-async-loop (https://www.npmjs.com/package/node-async-loop)
here is my code which i writing but whnever i want to response when the last loop completes it say can set header after send to cliend.
also in console log i am getting data every time when data coming from query.
const id = req.params.id;
finalData = [];
tb_user.findOne({ where: { id: id } }).then((userRiverSys, err) => {
if (userRiverSys) {
// console.log(userRiverSys.regionJson)
asyncLoop(userRiverSys.regionJson, function (item, next) {
// console.log("item", item);
tb_riverSystems.findAll(
{
where: { regionId: item.id }
}).then((findriverSys, err) => {
if (err) {
next(err);
return;
}
// console.log("findriverSys", findriverSys);
if (findriverSys) {
asyncLoop(findriverSys, function (item1, next1) {
if (err) {
next(err);
return;
}
// console.log("item1", item1.dataValues);
tb_facilities.findAll(
{
where: { riverSystemId: item1.dataValues.id }
}).then((findFacilities) => {
if (findFacilities) {
// console.log("findFacilities", findFacilities[0].dataValues.name);
asyncLoop(findFacilities, function (item2, next2) {
if (err) {
next(err);
return;
}
tb_userAccess.findAll(
{
where: { facilityId: item2.dataValues.id }
}).then((userAccessFacilities, err) => {
// console.log("userAccessFacilities", userAccessFacilities[0].dataValues);
// var i = 0;
asyncLoop(userAccessFacilities, function (item3, next3) {
finalData.push({
UserId: item3.userid,
facilityId: item3.facilityId,
})
next3();
},
function (err) {
if (err) {
console.error('Error: ' + err.message);
return;
}
// i++;
// console.log('Finished!!!!');
// if (userAccessFacilities.length === i) {
// console.log("finalData", i);
// // res.json({"status":"true", "message":"update OrgChallenge"})
// }
})
return res.json({"status":"true", "message":"update OrgChallenge"})
// console.log("finalData", finalData);
})
next2();
}, function (err) {
if (err) {
console.error('Error: ' + err.message);
return;
}
console.log('Finished!!!');
});
}
});
next1();
}, function (err) {
if (err) {
console.error('Error: ' + err.message);
return;
}
console.log('Finished!!');
});
}
});
next();
}, function (err) {
if (err) {
console.error('Error: ' + err.message);
return;
}
console.log('Finished!');
});
} else {
console.log("err3", err)
}
})
If you promisify your asynchronous action (so it returns a promise), then you can just use a regular for loop and async/await and there is no need for a 3rd party library to sequence your asynchronous loop. This is modern Javascript:
const { promisify } = require('util');
do.some.actionP = promisify(do.some.action);
async function someFunction() {
const array = ['item0', 'item1', 'item2'];
for (let item of array) {
let result = await do.some.actionP(item);
// do something with result here
}
return someFinalResult;
}
someFunction().then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
FYI, in real code, many (or even most) asynchronous operations now offer promisified versions of their API already so usually you don't even need to do the promisify step any more. For example, pretty much all databases already offer a promise interface that you can just use directly.
const loop = async (arr, results = []) => {
const item = arr.shift()
if (!item) {
console.log("DONE");
return results;
}
// as async function
await new Promise(resolve => {
resolve(results.push(`asynced-${item}`))
})
return loop(arr, results);
}
(async () => {
const result = await loop(["item0", "item1", "item2"])
console.log(result);
})();
I'd be happy if I can help you.
but this script uses a recursive function instead of node-async-loop.
so this might not be suitable for you.

how do I return documents back to my API call?

I am trying to call method get_radar_life_cycle from app.get("/api/radar_cloning and it throws the error shown below,
error is coming from line return done(null,documents) how do I return documents back to my API call?
METHODS:-
let get_cloned_radars = function(documents, done) {
let complete_radar_list=[]
for (let i = 0; i < documents.length; i++) {
complete_radar_list.push(documents[i]['orgRadar']);
for (let j = 0; j < documents[i]['clonedRadarsdetailslist'].length; j++) {
complete_radar_list.push(documents[i]['clonedRadarsdetailslist'][j]['clonedRadar']);
}
}
data = complete_radar_list
return done(null, data)
}
let get_radar_life_cycle = function(data,done) {
console.log("data after get_radar_life_cycle")
console.log(data)
Radar_life_cycle.find({orgRadar: {$in:data}})
.then(documents => {
console.log(documents) --> shows correct data
});
return done(null,documents) --> Error is coming from this line
};
API call:
app.get("/api/radar_cloning", (req, res, next) => {
Radar_cloning.find({orgRadar: {$in:req.query.params.split(',')}})
.then(documents => {
get_cloned_radars(documents, function(err,data) {
if (err) {
res.json(err);
if (data!=null){
console.log(data)
}//end for data
}//end of (Err)
});//get_cloned_radars
get_radar_life_cycle(data, function(err,radar_life_cycle_data) {
if (err) {
res.json(err);
console.log(radar_life_cycle_data)
}//end for radar_life_cycle_data
}//end of (Err)
});//end of get_radar_life_cycle
});
});
ERROR:-
(node:10065) UnhandledPromiseRejectionWarning: ReferenceError: documents is not defined
You are trying to access documents outside of its scope, the scope being everything between the { and }. So you cannot access it below the .then(() => {}) scope.
Luckily you are are providing a callback function called done(err, radar_life_cycle_data), which you can use anywhere in the scope of the get_radar_life_cycle(documents, done) function. Even the scopes inside of its scope. When you are calling the done function, what you are basically doing is calling this function (well it has some syntax errors, so I cleaned it up)
function(err,radar_life_cycle_data) {
if (err) {
res.json(err);
console.log(radar_life_cycle_data)
}
}//end for radar_life_cycle_data
which then gets executed
So the solution:
Move your done in your .then(() => {}) scope like this:
let get_radar_life_cycle = function(data,done) {
console.log("data after get_radar_life_cycle")
console.log(data)
Radar_life_cycle.find({orgRadar: {$in:data}})
.then(documents => {
console.log(documents) // --> shows correct data
done(null,documents) // --> No error coming from this line
});
};
Same goes for the data it is not in the scope of the get_cloned_radars
app.get("/api/radar_cloning", (req, res, next) => {
Radar_cloning.find({orgRadar: {$in:req.query.params.split(',')}})
.then(documents => {
get_cloned_radars(documents, function(err,data) {
if (err) {
res.json(err);
if (data!=null) {
console.log(data)
get_radar_life_cycle(data, function(err,radar_life_cycle_data) {
if (err) {
res.json(err);
console.log(radar_life_cycle_data)
} //end of (Err)
}); //end of get_radar_life_cycle
} //end for data
} //end of (Err)
}); //get_cloned_radars
});
But since your code is unreadable, here is a cleaned up version:
app.get("/api/radar_cloning", (req, res, next) => {
const radar_life_cycle_cb = function (err, data) {
if (err) {
res.json(err);
return;
}
console.log(data);
}
const cloned_radar_cb = function (err, data) {
if (err) {
res.json(err);
return;
}
if (data != null) {
get_radar_life_cycle(data, radar_life_cycle_cb);
}
};
Radar_cloning.find({orgRadar: {$in:req.query.params.split(',')}})
.then(documents => get_cloned_radars(documents, cloned_radar_cb));
}

How to get a function value on MongoDB collection.find()

When I run collection.find() in MongoDB/Node/Express, I need to return value for my array like this but iam in callback hell;
foursquare.getVenues(params,function(error, venues) {
if (!error) {
var places = [];
venues.response.venues.forEach(function(e) {
places.push(
{
obj_id:e.id,
name:e.name,
distance:e.distance,
here_now:req.collection.findById(e.id) //count- i want need this value
}
);
});
res.send(places);
}
});
You can try to use Async https://github.com/caolan/async#each
var async = require('async');
...
foursquare.getVenues(params, function (error, venues) {
if (!error) {
throw err;
}
var places = [];
async.each(venues.response.venues, function (e, callback) {
db.collection.findById(e.id, function (err, res) {
places.push({
obj_id: e.id,
name: e.name,
distance: e.distance,
here_now: res
});
callback()
});
}, function (err) {
if (err) {
console.log('A file failed to process');
} else {
console.log('All files have been processed successfully');
res.send(places);
}
});
});
or Using async.map
var async = require('async');
var createArray = function (e, cb) {
db.collection.findById(e.id,function(err,res){
var obj = {
obj_id: e.id,
name: e.name,
distance: e.distance,
here_now: res
}
cb(null, obj);
});
}
async.map(venues.response.venues, createArray, function (err, places) {
if(err){
throw err;
}
console.log(places);
});

Resources