Javascript Returning from within a nested function - javascript

I'm trying to return the output of the randomizer function within a route... I keep getting 'undefined' - but no idea what I'm doing wrong...
var randomizer = function() {
// A load of stuff happens here.. and functions that are needed by the pullOut function (I've removed for brevity)
var pullOut = function(pick) {
if (playerList.length !== pick) {
var random_item = getRandomItem(list, weight);
if (playerList.indexOf(random_item) == -1) { // doesn't exist. So add to array.
playerList.push(random_item);
}
pullOut(pick);
} else {
console.log(playerList)
return playerList;
}
}
return pullOut(pick);
}
router.route('/ordercreated')
.post(function(req, res) {
var orderedItems = req.body.line_items;
// I foreach through all the items - calling the randomizer function on each one...
_.forEach(orderedItems, function(n) {
Pack.findOne({
'product_id': n.variant_id
}, function(err, pack) {
if (err) {
return res.send(err);
}
if (pack) {
var list = [];
var weight = [];
_.forEach(pack.playerData, function(n) {
list.push(n.name);
weight.push(parseInt(n.chance));
});
console.log('randomizing', randomizer(pack.title, list, weight, n.qty, pack.pick));
}
});
});
res.sendStatus(200);
})

Your "pullOut" function calls itself, but it throws away the result of that call.
var randomizer = function() {
// A load of stuff happens here.. and functions that are needed by the
// pullOut function (I've removed for brevity)
var pullOut = function(pick) {
if (playerList.length !== pick) {
var random_item = getRandomItem(list, weight);
if (playerList.indexOf(random_item) == -1) { // doesn't exist. So add to array.
playerList.push(random_item);
}
return pullOut(pick); // <--- add return
} else {
console.log(playerList)
return playerList;
}
}
return pullOut(pick);
}
Without that return, when the function took that path through the main if statement it would return undefined.

Related

AngularJS filter with a promise

I'm upgrading a filter from a static object to an object retrieved from our database, but I can't seem to get a proper return. This filter takes in an integer which represents a named location, it looks up the location with the key, and returns the name. After troubleshooting, I'm getting close as I can see the object from the database and I can see some of the lookups correctly inside of the then section, but it's not being returned at the end of the filter. Is there a better method on getting this with a filter?
stPartFieldFilters.js
angular.module('app').filter('partLocation', function(stPartMgmtSvc, $q) {
var locs;
function getLocations() {
if(!locs) {
var dfd = $q.defer();
stPartMgmtSvc.getLocations().then(function(res) { locs = res; dfd.resolve(locs); }, function(response) { dfd.reject(response.data.reason) });
return dfd.promise;
}
else {
var dfd = $q.defer();
dfd.resolve(locs);
return dfd.promise;
}
}
function getResults(loc, type) {
var lr = null; // this should be updated once a match is found below
getLocations().then(function(ls) {
if (loc || loc === 0) {
loc = loc.split(',');
if(typeof loc === 'object') {
var res = new Array;
loc.forEach(function(l) {
res.push(ls[l].name)
});
if(!type) { lr = res.toString().replace(',', '<br>'); } // this line provides the correct output; see below (LOCFLTRRES1)
else { lr = res.toString().replace(',', ', '); }
}
else { lr = ls[loc].name; }
}
else { lr = false; };
});
return lr; // return the updated result
}
return function(loc, type) {
return getResults(loc, type); //return the final result for the filter
}
});
inventory.jade
~~~
td.desktop-only {{part.partLoc | partLocation:1}}
~~~
Here is an image of the results of the database object.
Here is an image of the result inside of the filter. This should be set to lr and returned to the main filter function.
Here is the expected result.
Finally, here is the actual result.

Unable to return result from promise

I am trying to read some data from 2 different tables and parse a CSV file before rendering an ejs file.
I can get the data from both tables and from the CSV file but I seem to be unable to return the result.
Pretty sure this is a problem with the way I handle async execution but I fail to see what I am doing wrong.
I've spent the last 2 days reading about this (including the threads around here) and browsing but somehow the answer still escapes me.
First file - usercms.js
app.get('/userscms', function(req, res)
{
existingUsers.getExistingUsers()
.then(function(appUsers)
{
//global users array
//I can display these in my ejs file
globalAppUsers = appUsers;
})
.then(existingUsersAttributesQlik.getExistingUsersAttributesQlik())
.then(function(usersQlikAttributes)
{
//global user attributes array
//undefined data
globalUsersQlikAttributes = usersQlikAttributes;
})
.then(existingSuppliers.parseSuppliersCSV())
.then(function(supplierData)
{
//the result I am expecting
//this prints undefined
console.log(supplierData);
}).then(function()
{
res.render('userscms.ejs',
{
users: globalAppUsers,
attributes: globalUsersQlikAttributes
});
});
});
Second function - getxistingUsers.js (identical to the getExistingUsersAttributesQlik, except for the query)
var userData = [];
var appUsers = [];
(function (exports)
{
exports.getExistingUsers = function ()
{
return promisemysql.createConnection(dbconfig.development).then(function(conn)
{
var result = conn.query("SELECT id, username, firstName, lastName, email, phone, lastLogin, isAdmin, isValid, isPhoneValid, accountCreationDateTime FROM Users");
conn.end();
return result;
}).then(function(rows)
{
return rows;
}).then(function(rows)
{
if (rows.length)
{
userData = [];
appUsers = [];
rows.forEach(function (elem)
{
userData.push(_.toArray(elem));
});
for (i = 0; i < userData.length; i++)
{
var appUser = new appUserModel.AppUser(
userData[i][0],
userData[i][1],
userData[i][2],
userData[i][3],
userData[i][4],
userData[i][5],
userData[i][6],
userData[i][7],
userData[i][8],
userData[i][9],
userData[i][10]);
appUsers.push(_.toArray(appUser));
}
return appUsers;
}
else
{
console.log("NOPE");
return null;
}
}).then(function(appUsers)
{
console.log(appUsers);
return appUsers;
});
};
})(typeof exports === 'undefined' ? this['getExistingUsers'] = {} : exports);
Third file - parseSuppliersCSV.js
var supplierData = [];
var suppliersData = [];
var csvCount = 0;
(function (exports)
{
exports.parseSuppliersCSV = function ()
{
return new Promise(function(resolve, reject)
{
var fileStream = fs.createReadStream("myCSV.csv");
var parser = fastCsv();
csvCount = 0;
supplierData = [];
suppliersData = [];
fileStream
.on("readable", function ()
{
var data;
while ((data = fileStream.read()) !== null)
{
parser.write(data);
}
})
.on("end", function ()
{
parser.end();
});
parser
.on("readable", function ()
{
var data;
while ((data = parser.read()) !== null)
{
if(csvCount >= 1)
{
csvCount++;
var arrayOfStrings = data[0].split(';');
var supplier = new supplierModel.Supplier(arrayOfStrings[0],arrayOfStrings[1]);
suppliersData.push(_.toArray(supplier));
}
else
{
csvCount++;
}
}
})
.on("end", function ()
{
console.log("done");
//all OK here
console.log(suppliersData);
//this doesn't seem to return anything
return suppliersData;
});
});
};
})(typeof exports === 'undefined' ? this['parseSuppliersCSV'] = {} : exports);
Any ideas what I am doing wrong? Am I approaching this the wrong way?
I'll take a guess here and assume the promise you created should resolve to something...instead of returning a value.
.on("end", function ()
{
console.log("done");
//all OK here
console.log(suppliersData);
//this doesn't seem to return anything
return resolve(suppliersData);
});

Node js synchronize each loop

On my Node JS backend I run this method.
var locations = [];
exports.constructionsiteParser = function constructionsiteParser(response){
var timestamp = new Date().toDateInputValue();
const $ = cheerio.load(response);
$('situation').each( function(){
var situation = [];
$(this).find('situationRecord').each( function(i){
var startLocationCode = $(this).find('alertCMethod2SecondaryPointLocation').find('specificLocation').text();
var endLocationCode = $(this).find('alertCMethod2PrimaryPointLocation').find('specificLocation').text();
var overallStartTime = $(this).find('overallStartTime').text();
var overallEndTime = $(this).find('overallEndTime').text();
if((startLocationCode != '') && new Date(timestamp) >= new Date(overallStartTime) && new Date(timestamp) <= new Date(overallEndTime) ){
Promise.all([
locationCodeToGeodataRequst.geodataByLocationcode(startLocationCode),
locationCodeToGeodataRequst.geodataByLocationcode(endLocationCode)
]).then( values =>{
return createSituationRecord($, this, startLocationCode, endLocationCode, values[0], values[1]);
}).then( function(obj){
console.log("before push", situation);
situation.push(obj);
console.log("after push", situation);
return situation;
}, handleError);
}
})
console.log("from outter", situation.length);
if(situation.length > 0){ //if situation is not empty
locations.push(situation);
}
})
console.log(locations);
}
The console.log("from outter", situation.length); at the bottom prints always 0
also the console.log(locations) is empty
This is a part of the log:
...
from outter 0
from outter 0
from outter 0
from outter 0
from outter 0
[]
before push []
after push [....
I think this happens because the node server runs the bottom part before the inner each loop finishes. So I want to make it more snychronized. What I want to do is something like:
outer each{
//run this first
inner each{
.....
}
//if inner each is done run this
if(...){}
}
But I don't know how to put this in the correct syntax.
I have tried it with nested Promises but it doesn't work.
you can return this promise. deal it at caller
You can make use of async.eachOf(). I took a different approach in making your code synchronous. Hope it helps you.
'use strict';
let locations = [];
exports.constructionsiteParser = function constructionsiteParser(response) {
const $ = cheerio.load(response);
$('situation').each(function () {
let situation = [];
async.eachOf($(this).find('situationRecord'), function (value, key, callback) {
innerLoop(callback);
}, function (err, situation) {
if (err) {
return console.error(err.message);
}
console.log("from outter", situation.length);
// this will run only if the inner loops completes
if (situation.length > 0) { //if situation is not empty
locations.push(situation);
}
});
});
console.log(locations);
};
function innerLoop(callback) {
let startLocationCode = $(this).find('alertCMethod2SecondaryPointLocation').find('specificLocation').text();
let endLocationCode = $(this).find('alertCMethod2PrimaryPointLocation').find('specificLocation').text();
let overallStartTime = $(this).find('overallStartTime').text();
let overallEndTime = $(this).find('overallEndTime').text();
if (isInvalid(startLocationCode, overallStartTime, overallEndTime)) {
return callback('some error msg');
}
Promise.all([
locationCodeToGeodataRequst.geodataByLocationcode(startLocationCode),
locationCodeToGeodataRequst.geodataByLocationcode(endLocationCode)
]).then(values => {
return createSituationRecord($, this, startLocationCode, endLocationCode, values[0], values[1]);
}).then((obj) => {
return callback(null, obj);
}).catch((err) => {
console.log('err', err.stack);
return callback(err);
});
}
function isInvalid(startLocationCode, startTime, endTime) {
let timestamp = new Date().toDateInputValue();
let isEmptyCode = startLocationCode === '';
let isYetToStart = new Date(timestamp) < new Date(startTime);
let isOver = new Date(timestamp) > new Date(endTime);
return isEmptyCode || isYetToStart || isOver;
}
You should take a deeper look into promises because they are the way to go for synchronous operations. Maybe try to merge your code into functions.

Boolean fileContainsString not working

I have a function that returns a boolean of whether or not a file contains a string. I know the checkFileForString function runs and properly assigns the boolean value as true but the code within the if statement at the bottom does not run.
var checkFileForString = function (fileName) {
var yesStringFound = false;
fs.readFile(fileName, function (err, data) {
if (err) throw err;
if(data.indexOf('yes') >= 0){
yesStringFound = true;
}
console.log('YES STRING FOUND: ' + yesStringFound);
});
return yesStringFound;
};
console.log("instanceCount >= 4...");
if (checkFileForString(quadrantFile0) == true) { //todo: confirm and fix
console.log("runs...");
assignToQuadrant(0, maxInstances);
quadrantOccupied = 0;
setQuadrantAsUnavailable(quadrantOccupied);
}
Solution I got with help from #nnnnnn:
var checkFileForString = function (fileName) {
var yesStringFound = false;
var jsonFile = fs.readFileSync(fileName);
if(jsonFile.indexOf('yes') >= 0)
yesStringFound = true;
return yesStringFound;
};

Nodejs and forEach

Okay so i know that it is bad pratice to force node.js to be syncronous but in this case i have no choice.
I am trying to create a tree like structure of my categories for this i have created this function:
router.route('/api/categoryStructure')
.get(function (req, res) {
var cat = Category.build();
cat.getRootCategories(function (categories) {
var result = [];
var root = categories;
root.forEach(function (y) {
var tmp = findTree(y);
result.push(tmp);
});
})
});
function findTree(rootCategory) {
var root = [rootCategory];
var result = [];
(function loop() {
var element = root[0];
var category = Category.build();
category.retrieveByParentId(element.id, function (categories) {
if (categories) {
element.dataValues.subcategories = categories;
categories.forEach(function (division) {
root.push(division);
});
root.splice(0, 1);
if (result.length == 0) {
result.push(element);
loop()
}
else if (root.length == 0) {
return result;
}
else {
loop()
}
}
else
{
result = root;
return result;
}
});
}());
}
Now as you can see it loop through each of the root categories to find all subcategories and all of their subcategories.
This works perfectly fine however there is a problem
The tmp variable in my loop is set to undefined because of the asyncronous behavior of node. This means that my array is being filled up with undefined/ null values.
So my question is how can i avoid this?
First solution:
Lets add some logic to findTree to make it accepts callbacks
function findTree(rootCategory, callback) {
var root = [rootCategory];
var result = [];
(function loop() {
var element = root[0];
var category = Category.build();
category.retrieveByParentId(element.id, function (categories) {
if (categories) {
element.dataValues.subcategories = categories;
categories.forEach(function (division) {
root.push(division);
});
root.splice(0, 1);
if (result.length == 0) {
result.push(element);
loop()
}
else if (root.length == 0) {
callback(result);
}
else {
loop()
}
}
else
{
result = root;
callback(result);
}
});
}());
}
then you can now call findTree with a callback having any logic you want to be executed secondly.
findTree(y,function(data){
result.push(data);
});
Another way using async module.
You could use async module . Its auto function is awesome . If you have function A() and function B() and function C() . Both function B() and C() depend of function A() that is using value return from function A() . using async module function you could make sure that function B and C will execute only when function A execution is completed .
Ref : https://github.com/caolan/async
async.auto({
A: functionA(){//code here },
B: ['A',functionB(){//code here }],
C: ['A',functionC(){//code here }],
D: [ 'B','C',functionD(){//code here }]
}, function (err, results) {
//results is an array that contains the results of all the function defined and executed by async module
// if there is an error executing any of the function defined in the async then error will be sent to err and as soon as err will be produced execution of other function will be terminated
}
})
});

Categories