My question is related to following code -
Scenario 1:
var queue = [];
var busy = false;
exports.getAProfile = function (params, cb) {
queue.push({params: params, cb: cb});
if (!busy) {
checkQueue(function (c, next) {
service.getAProfileNow(c.params, function (err, res) {
c.cb(err,res);
next();
});
});
}
}
function checkQueue(actualTask) {
var c = queue.shift();
if (c) {
busy = true;
actualTask(c, function () {
checkQueue(actualTask);
})
return queue.length;
} else {
busy = false;
return queue.length;
}
}
Scenario 2:
var queue = [];
var busy = false;
var actualTask = function (c, next) {
service.getAProfileNow(c.params, function (err, res) {
c.cb(err,res);
next();
});
}
exports.getAProfile = function (params, cb) {
queue.push({params: params, cb: cb});
if (!busy) {
checkQueue();
}
}
function checkQueue() {
var c = queue.shift();
if (c) {
busy = true;
actualTask(c, function () {
checkQueue(actualTask);
})
return queue.length;
} else {
busy = false;
return queue.length;
}
}
Difference in both codes are -
The function actualTask is (i guess) defined many times in first scenario while in Second scenario it is defined only once.
is 'what I interpret' correct? then which one is better? and why? is performance of code different in both?
Note: service module is require-d in code, it only queries database.
Defining function just once does not make the performance or execution fast. It will just reduce the file size of your code and make your code formation pretty well. In both cases, the function must have to jump to procedure call.
Related
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);
});
How do I iterate over a asynchronous function in nodejs stopping depending on callback return?
Example:
suppose I have following function
var fn1 = function(i, callback){
// code here
callfunction(i, function(err, p2){
if(err) throw err;
if(something) return callback(true);
else return callback(false);
});
}
I need to iterate and call for i=1,2,3,... until my callback returns false.
I tried using async whilst, but I do not think it can helps me, because the test function also needs callback, and the function for test and iteratee are same, I cannot call twice.
If I understand your problem correctly. What about some recursion
const maxTries = 10;
// just a stupid counter to run a be able to change return value
let numberOfCalls = 0;
const asyncCall = () => {
if (numberOfCalls === 9) {
return Promise.resolve(false);
}
numberOfCalls += 1;
return Promise.resolve();
};
function iterate(calls = 0) {
if (maxTries === calls) {
return Promise.resolve();
}
return asyncCall().then(value => {
if (value === false) {
return value;
}
return iterate(calls + 1);
});
}
iterate().then(result => {
if (result === undefined) {
//to many tries
}
console.log('result', result);
});
When I get a request, I want it to generate a 4-character code, then check if it already exists in the database. If it does, then generate a new code. If not, add it and move on. This is what I have so far:
var code = "";
var codeFree = false;
while (! codeFree) {
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var code = "";
for (var i = 0; i < 4; i++) {
var rand = Math.floor(Math.random() * chars.length);
console.log(rand);
code += chars.charAt(rand);
}
console.log("Code: %s generated.", code);
client.execute("select * from codes where code=" + code, function(err, result) {
if (! err) {
if (result.rows.length > 0) {
codeFree = false;
} else {
codeFree = true;
}
} else {
console.log('DB ERR: %s', err);
}
console.log(codeFree);
});
console.log('here');
}
This does not do nearly what I want it to do. How can I handle something like this?
You are doing an async task.
When you have an asyncronous task inside your procedure, you need to have a callback function which is going to be called with the desired value as its argument.
When you found the free code, you call the function and passing the code as its argument, otherwise, you call the getFreeCode function again and passing the same callback to it. Although you might consider cases when an error happens. If your the db call fails, your callback would never get called. It is better to use a throw/catch mechanism or passing another argument for error to your callback.
You can achieve what you need to do by doing it this way:
function getFreeCode(callback) {
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var code = "";
for (var i = 0; i < 4; i++) {
var rand = Math.floor(Math.random() * chars.length);
console.log(rand);
code += chars.charAt(rand);
}
console.log("Code: %s generated.", code);
client.execute("select * from codes where code="+code, function(err, result) {
if(!err) {
if(result.rows.length > 0) {
getFreeCode(callback);
} else {
callback(code);
}
}else {
console.log('DB ERR: %s', err);
}
console.log(codeFree);
});
console.log('here');
}
// in your main:
getFreeCode(function (code) {
console.log(' this code was free: ' + code)
})
I recommend you look into two alternatives to help deal with asynchronous code.
node generator functions using the 'yield' keyword
promises
Using generators requires running a recent version of node with the --harmony flag. The reason I recommend generators is because you can write code that flows the way you expect.
var x = yield asyncFunction();
console.log('x = ' + x);
The previous code will get the value of x before logging x.
Without yielding the console.log would write out x before the async function was finished getting the value for x.
Your code could look like this with generators:
var client = {
execute: function (query) {
var timesRan = 0;
var result = [];
return function () {
return setTimeout(function () {
result = ++timesRan < 4 ? ['length_will_be_1'] : [];
return result;
},1);
};
}
};
function* checkCode () {
var code;
var codeFree = false;
while(!codeFree) {
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
code = "";
for (var i = 0; i < 4; i++) {
var rand = Math.floor(Math.random() * chars.length);
console.log(rand);
code += chars.charAt(rand);
}
console.log("Code: %s generated.", code);
try {
var result = yield client.execute("select * from codes where code="+code);
codeFree = result.rows.length > 0 ? false : true;
}catch(e) {
console.log('DB ERR: %s', err);
} finally {
console.log(codeFree);
}
console.log('here');
}
}
checkCode().next();
You would leave off the client object. I only added that to make a working example that fakes an async call.
If you have to use an older version of node or do not like the yield syntax then promises could be a worthy option.
There are many promise libraries. The reason I recommend promises is that you can write code that flows the way you expect:
asyncGetX()
.then(function (x) {
console.log('x: ' + x);
});
The previous code will get the value of x before logging x.
It also lets you chain async functions and runs them in order:
asyncFunction1()
.then(function (result) {
return asyncFunction2(result)
})
.then(function (x) { /* <-- x is the return value from asyncFunction2 which used the result value of asyncFunction1 */
console.log('x: ' + x);
});
Your code could look like this with the 'q' promise library:
var Q = require('q');
var client = {
timesRan: 0,
execute: function (query, callback) {
var self = this;
var result = {};
setTimeout(function () {
console.log('self.timesRan: ' + self.timesRan);
result.rows = ++self.timesRan < 4 ? ['length = 1'] : [];
callback(null, result);
},1);
}
};
function checkCode () {
var deferred = Q.defer();
var codeFree = false;
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var code = "";
for (var i = 0; i < 4; i++) {
var rand = Math.floor(Math.random() * chars.length);
console.log('rand: %s', rand);
code += chars.charAt(rand);
}
console.log("Code: %s generated.", code);
client.execute("select * from codes where code="+code, function(err, result) {
console.log('err: '+err+', result: ' + JSON.stringify(result));
console.log('result.rows.length: ' + result.rows.length);
if(!err) {
if(result.rows.length > 0) {
codeFree = false;
console.log('result.rows: %s, codeFree: %s', result.rows, codeFree);
checkCode();
} else {
codeFree = true;
console.log('line 36: codeFree: ' + codeFree);
deferred.resolve(code);
}
}else {
console.log('DB ERR: %s', err);
deferred.reject(err);
}
console.log(codeFree);
});
console.log('waiting for promise');
return deferred.promise;
}
checkCode()
.then(function (code) {
console.log('success with code: ' + code);
})
.fail(function(err) {
console.log('failure, err: ' + err);
});
Also omit the client object here. I only added that to make a working example that fakes an async call.
Promises and generators definitely take some time to get used to. It's worth it because they make the code a lot easier to follow in the end than code written with nested callbacks.
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
}
})
});
As I understand it the 100 asyncFunctions in the code below will not be executed until after func has returned true, and at this point the referens to i will not be valid. Nothing in this code would work as expected I guess.
Psedo example code:
function func(){
var needToKnow = []
for i = 1 to 100 {
needToKnow[i] = asyncFunction( i )
}
//Do some work on needToKnow[i]
return true
}
What would be the Javascript way to do something like this?
Use callbacks:
function func(callback) {
var needToKnow = [],
max = 100;
for (var i = 0; i < max; i++) {
asyncFunction(i, function (result) {
needToKnow.push(result);
if (needToKnow.length == max) { // or something that let you know that its finished
callback(needToKnow);
}
});
}
}
function asyncFunction(i, callback) {
setTimeout(function () {
callback({ index: i });
}, 1000); // Im an async func!
}
And use it this way:
func(function (result) {
console.log(result);
});
Be careful, don't get in callback hell
Here is an example using the Q Promise library:
function functionThatCouldThrowError(i){
//It doesn't, but just to give an idea of error propagation.
return { index: i };
}
function asyncFunction(i) {
var deferred = Q.defer();
setTimeout(function () {
try{
var data = functionThatCouldThrowError(i);
deferred.resolve(data);
} catch (error) {
deferred.reject({ index: i, error: error });
}
}, 1000);
return deferred.promise;
}
function doAll() {
var needToKnow = []
for (var i = 0; i < 100; i++) {
needToKnow[i] = asyncFunction( i );
}
return Q.all(needToKnow);
}
doAll().then(function(arg) {
//arg contains all 100 elements
alert("All done");
})
Update: expanded the example to demonstrate how to handle errors.
Plunker:http://plnkr.co/edit/djWpTKxgvzK2HmkVwvTy?p=preview