JS Lambda scoping with async.parallel call - node.js

I am unable to capture a variable into the lambda function from its outer scope. When I run this code it executes with the same variable. I am passing the variable into the function, but I am obviously misunderstanding how scoping works with lambda.
// Add tasks to async_tasks
for(var i = 0; i < 10; i++){
var task = function(task_callback){
// I want to capture i and pass it to the function
(function(i){
exports.defaultCarWithId(connection, i, function(err, data){
if (err) {
console.log('error in query: ' + err.stack);
fCallback("[Internal Server Error]", null);
return;
}
task_callback();
});
})(i);
};
async_tasks.push(task);
}
// execute tasks
async.parallel(async_tasks, function(err, results){
fCallback(null, user);
return;
});

You can wrap the outer function:
var task = (function(i){
return function(task_callback){
exports.defaultCarWithId(connection, i, function(err, data){
if (err) {
console.log('error in query: ' + err.stack);
fCallback("[Internal Server Error]", null);
return;
}
task_callback();
});
};
})(i);
async_tasks.push(task);
or pass i as argument and bind it:
var task = function(i, task_callback){
exports.defaultCarWithId(connection, i, function(err, data){
if (err) {
console.log('error in query: ' + err.stack);
fCallback("[Internal Server Error]", null);
return;
}
task_callback();
});
};
async_tasks.push(task.bind(null, i));

Basically two mistakes i can see:
You are re-declaring the variable i inside for loop. Name it something different.
Create closure something like this:
Var task = (function(i) {
return function(task_callback) {
// Your logic
};
})(i);
Hope it'll help.

It's hard to understand what exactly you are calling lambda here but if you want to bind context in async you need to use method each or one of new array methods of a framework. Have a look to docs

Related

Idiomatic Node error handling

So I am doing a nodejs tutorial and it asks me to use modules to filter all the files in a directory. And I'm supposed to use the idiomatic approach to handle errors. Below is my modules.js and my main program.js, however, the program said that
Your additional module file [module.js] does not appear to pass back an
error received from fs.readdir(). Use the following idiomatic Node.js
pattern inside your callback to fs.readdir():
if (err) return
callback(err)
but I did handle the error on the first line using if (err)return callback(err);
Can someone please point out what I am doing wrong or what best practice I'm not following? Thanks
module.exports = function filterList(dirName, extName, callback) {
fs.readdir(dirName, function callback(err, list) {
if (err)
return callback(err);
for (var i = 0; i < list.length; i++) {
if (path.extname(list[i]) == '.' + extName) {
callback(null, list[i]);
}
};
});
}
my program.js is as follows
var myMod = require('./module');
function printOut(err, result) {
if (err) {
console.log(err);
};
console.log(result);
}
myMod(process.argv[2], process.argv[3], printOut);
You have two functions named callback here which is causing unexpected behavior.
Your main exported function takes an argument name callback. Then inside that you define another function named `callback':
function filterList(dirName, extName, callback){ // <-- callback as arg
fs.readdir(dirName, function callback(err, list) { // <-- callback defined again
if (err)
return callback(err); // <-- which function is this calling?
/* etc. */
}
When you finally return callback(err) you are calling the wrong function. You want to call the first one -- the one passed into filterList(), but the second one is in scope.
You could instead pass an anonymous function to fs.readdir since you never need to call it:
fs.readdir(dirName, function(err, list) {
if (err)
return callback(err); // <-- now there's only one call back
Now it's clear that you are calling the correct callback and it's more idiomatic.
You're shadowing your callback by naming the function the same as the argument. Try this:
module.exports = function filterList(dirName, extName, callback) {
fs.readdir(dirName, function cb(err, list) {
if (err) return callback(err);
for (var i = 0; i < list.length; i++) {
if (path.extname(list[i]) == '.' + extName) {
callback(null, list[i]);
}
};
});
}
Notice the rename of the second paramerter to fs.readdir is now named cb, you don't actually need to name it, but it does help for stack traces and logging.
One other thing, you are going to have an issue calling callback inside a loop. There are ways to break out of it and also ways to avoid having it in the loop.

callback is not a function node js

I am new to javascript and i am having trouble solving this error. I get the message: "callback is not a function" at:"return callback(rolesArray)".
Rol.getAllRoles = function(callback){
sql = "select role from Role;";
var rolesArray = [];
var role;
mysql.connection(function(err,conn){
if (err){
return callback(err);
}
conn.query(sql,function(err,rows){
if (err){
return callback(err);
}
for(var i=0; i < rows.length; i++){
role = rows[i].role;
rolesArray.push(rol);
}
console.log("roles: " + rolesArray);
return callback(rolesArray);
});
});
}
The console.log outputs:"roles: admin,customer" so the connection with the database works.
That error means that you are not passing a function to Rol.getAllRoles(fn) when you call it.
In addition, so that you can have proper error handling in your callback and so you can more easily distinguish between an error and the actual data, you should always pass a first argument to the callback that indicates whether there was an error or not and then the second argument (if not an error) can be your results array like this:
Rol.getAllRoles = function(callback){
sql = "select role from Role;";
var rolesArray = [];
var role;
mysql.connection(function(err,conn){
if (err){
return callback(err);
}
conn.query(sql,function(err,rows){
if (err){
return callback(err);
}
for(var i=0; i < rows.length; i++){
role = rows[i].role;
rolesArray.push(rol);
}
console.log("roles: " + rolesArray);
// make sure the first argument to the callback
// is an error value, null if no error
return callback(null, rolesArray);
});
});
}
And, then you should be calling it like this:
Rol.getAllRoles(function(err, rolesArray) {
if (err) {
// handle error here
} else {
// process rolesArray here
}
});
This style of calling an async callback as in callback(err, data) is a very common async callback design pattern. It allows all callers to see if there was an error or not and if there was no error to get access to the final result.
I'd suggest the following:
Rol.getAllRoles = function(callback){
var sql = "select role from Role;";
var rolesArray = [];
var role;
callback = callback || function(){};
mysql.connection(function(err,conn){
if (err){
return callback(err);
}
conn.query(sql,function(err,rows){
if (err){
return callback(err);
}
for(var i=0; i < rows.length; i++){
role = rows[i].role;
rolesArray.push(rol);
}
console.log("roles: " + rolesArray);
return callback(rolesArray);
});
});
}
This way you enforce that callback is always a function. If you run it like Rol.getAllRoles() then you would get an error previously. Now you wont. You wont get any data back though.
Make sure you are calling Rol.getAllRoles with the proper parameter (ie: a function).

Searching then adding record in Redis using Node.js

I may be over tired but for the life of me I cannot understand why the following is not working. I am trying to search if a string exists and if it does not, add it to a redis database
options = options || {};
var counter = 1,
client = redis.getClient();
options.name = options.name || '';
if (_.isEmpty(options.name)) {
return callback('Cannot add name. No name supplied');
} else {
options.name = options.name.trim();
}
client.get('mySavedKeys' + options.name, function (err, data) {
if (err) {return callback(err); }
if (!_.isNull(data)) {
console.log('Name found', options.name);
return callback(null, data);
} else {
counter += 1;
console.log('Name not found', options.name);
console.log('ID', counter)
client2.set('mySavedKeys' + options.name, counter, function (err) {
if (err) {return callback(err); }
console.log('Added', options.name);
return callback(null, counter);
});
}
});
If I run an array of names to add using async.each then it seems to run all the 'get' functions and then run the 'set' function so I am getting duplicate insertions.
I'm sure the answer is obvious but I cannot see the problem.
If you use async.eachSeries you would insure that the get/set happen atomically rather than all gets running in parallel.

Async node.js data flow confusion

thanks for your help...struggling big time with how to handle this properly. I'm in async now, having given up on my ability to write the callbacks properly. I have snippet where I'm passing a set of random numbers (eachrecord) and passing them through to a mongoose call. Trying to create a data set from the multiple queries I pass.
My issue is that no matter what I've done for 4 hours, the "newarray" variable is always empty.
Thank you for your help -
async.forEach(arLimit, function(eachrecord, callback){
newarray = new Array;
var query = UGC_DB_Model.find({}).skip(eachrecord).limit(-1);
query.execFind(function (err, data) {
if (err)
console.log(err);
else {
newarray.push(data);
}
});
callback(null, newarray);
}, function(err, result) {
if (err) return next(err);
console.log("(it's empty): " + result);
});
There are several issues with your code:
async.forEach isn't meant to 'generate' results, that's what async.map is for;
you need to call the callback only when execFind is done, and not immediately after calling it;
your newarray is probably not necessary;
So try this instead:
async.map(arLimit, function(eachrecord, callback){
var query = UGC_DB_Model.find({}).skip(eachrecord).limit(-1);
query.execFind(function (err, data) {
if (err)
callback(err); // pass error along
else {
callback(null, [ data ]);
// although I think you mean this (because 'data' is probably an array already)
// callback(null, data);
}
});
}, function(err, result) {
if (err) return next(err);
console.log("(it's empty): " + result);
});

node.js multiple SerialPorts

I am using the SerialPorts module for nodejs and need to be able to open, write and read from a variable number of serial ports.
So what I am doing is to first create an array object for the serialPort instances, and then process them in a loop:
var serialport = require("serialport");
var SerialPort = serialport.SerialPort; // localize object constructor
var devs = ["/dev/tty.SerialPort","/dev/tty.HHW-SPP-1800-2-DevB"];
var ports = [];
for (var i = 0; i < devs.length; i++) {
console.log(devs[i]);
var port = new SerialPort(devs[i],{ baudrate:9600, parser: serialport.parsers.readline("\n") });
ports.push(port);
}
Then I have another function that I call periodically to read / write from the ports:
function minute(){
for (var i = 0; i < ports.length; i++) {
console.log(i);
ports[i].on("open", function (path) {
console.log('opened');
ports[i].write("Helo World\n", function(err,res) {
if(err) console.log('err ' + err);
console.log('results ' + res);
});
ports[i].on("data", function (data) {
console.log("here: "+data);
});
});
}
}
The problem is the minute() function executes, however it does not attempt to open or read / write to the ports.
What am I doing wrong ?? and is there a better way of doing this ??
There are a couple misconceptions at play here.
Firstly, you don't need to periodically poll your ports. Nodejs uses an event loop (more or less), to handle IO, and will do the polling for you. So all you need to do is setup the callbacks for the open event, one time for each port. In your code, it looks like you are readding the callback each time minute() is being called. That is not necessary.
Secondly, javascript doesn't have block scoping for variables. Instead you are inadvertently creating a closure, and your code is in error. In this following block:
for (var i = 0; i < ports.length; i++) {
ports[i].on("open", function (path) {
ports[i].write("Helo World\n", function(err,res) {
if(err) console.log('err ' + err);
console.log('results ' + res);
});
ports[i].on("data", function (data) {
console.log("here: "+data);
});
});
}
When your callback for ports.on is invoked, the value of i in ports[i].write and ports[i].on("data") isn't the value of i when the callback is setup, as you are expecting. Instead, because you have created a closure, the value of i isn't bound(set) until the callback is executed. In this example, everyone of your callbacks, i will be set to ports.length, which was the last evaluated value for i
I've created a plunkr that illustrates the problem with your for loop.
One way to fix this problem is to use an anonymous method, and bind the value i to a new local variable. In the code below, (function(index){})(i); executes immediately, and binds the value index to the appropriate value of i.
ports[i].on("open", function (path) {
(function(index) {
ports[index].write("Helo World\n", function(err,res) {
if(err) console.log('err ' + err);
console.log('results ' + res);
});
ports[index].on("data", function (data) {
console.log("here: "+data);
});
})(i);
});
You could also instead pull that method out into a separate function. setupHandlers() executes immediately, and is bound to the proper port.
for (var i = 0; i < ports.length; i++) {
setupHandlers(ports[i]);
}
function setupHandlers(port) {
port.on("open", function (path) {
ports.write("Helo World\n", function(err,res) {
if(err) console.log('err ' + err);
console.log('results ' + res);
});
ports.on("data", function (data) {
console.log("here: "+data);
});
});
}

Resources