node.js passing value to a variable in async mode - node.js

My problem is the following:
I have a nice folder crawler function, which grabs the paths' of the files. I'm (I would like to) use these files for testing purposes.
1.) Grabbing the files
2.) Do some testing
3.) Job Done
This is the code where I call it:
walk(csvRoot, function(err, results){
if (err) throw err;
for (var i = 0; i < results.length; i++) {
return results[i] // - not working
}
});
My main issue is, that I really would like to pass the results to a variable, which will contain these paths as an array, but so far no luck.
The variable returns as undefined, that's what I'm trying to resolve currently.
Could you please advise how to do so?

Why do you use return within the for loop? What do you expect to return there? In any case, if you are expecting to have the results available outside of the scope of the walk function, it will not work. I pressume that you need something like that:
function getFiles (csvRoot, callback) {
walk(csvRoot, function (err, results) {
if (err) {
return callback(err);
}
return callback(null, results);
});
}
getFiles(csvRoot, functions (err, files) {
// #todo: check for error
console.log(files);
});

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.

node.js svn update/commit synchronously?

I'm using svn-spawn library to update/commit files to svn. Problem is my app calls svn up/commit in a loop, and because of the async nature of the call, svn-up is called from the next iteration of the loop before the previous svn-up can finish.
How to handle this issue? Is there any way to prevent the next call from happening until the previous one is complete?
Figured out a way to do it using async module.
async.series can be used to execute async tasks in a serial fashion.
This is how I did it.
function commitFile(arg, callback) {
svnClient.getStatus(filePath, function(err, data) {
//...
svnClient.commit(['Commit msg', filePath], callback);
//...
});
}
var toCommit = [];
for (var i = 0, len = requests.length; i < len; i++) {
//Adding files to commit, async.apply enables adding arguments to the anonymous function
toCommit.push(async.apply(function(arg, cb) {
commitFile(arg, cb);
}, 'arg1'));
}
async.series(toCommit,function (err, result) {
console.log('Final callback');
if(err) {
console.log('error', err);
} else {
console.log('result of this run: ' + result);
}
});
async.series needs an array of functions which must call a callback once they are done. It uses the callback to determine that the current function in done executing and only then it will pick the next function to execute.

NodeJS and parallel flow

I'm new with NodeJS. An issue makes me confused is parallel flow. I read an example show this snippet as a technique for controlling parallel flow:
var fs = require('fs');
var fileDir = './files';
fs.readdir(filesDir, function (err, files) {
if (err) throw err;
for (var index in files) {
var task = (function (file) {
return function () {
fs.readFile(file, function (err, text) {
if (err) throw err;
doSomething();
});
}
})(filesDir + '/' + files[index]);
tasks.push(task);
}
for (var index in tasks) {
tasks[index]();
}
});
This code work like a charm, but when I replace it with
for (var index in files) {
var task = function () {
console.log(files[index]);
fs.readFile(filesDir + '/' + files[index], function (err, text) {
if (err) throw err;
doSomething();
});
};
tasks.push(task);
}
for (var index in tasks) {
tasks[index]();
}
It doesn't work as I expected, because the files[index] in loop is always the last file in directory. Could you please explain me what the real flow is?
In short, the function you created have reference for the index variable(not it's value), so when it's executed, the index value is the last file in directory in your case.
Some links: Understanding variable capture by closures in Javascript/Node
Its because index reference will be to its last file. Node js is asynchronous that it ll not wait till read file operation is completed. It ll increment index value.
for (var index in files) {
var task = function () {
console.log(files[index]);
fs.readFile(filesDir + '/' + files[index], function (err, text) {
if (err) throw err;
doSomething();
});
};
tasks.push(task);
}
Since first code uses closures and it passes the current indexed file to a function. It ll take the current indexed file and returns a function with the file as input.
Now that returned function will execute in parallel.

how can return value with require file in node js?

I am new to node.js , and use ari client.
I have two .js files that first one is required in second one. I have declared a variable in second one that must contain return value of first file but console.log(variable) is undefined although in first file return value is not null.
in first file :
var db = require("./database.js");
var service_info = db.select(connection,'select * from services where ivr_shortcode = ?',service);
console.log(service_info);
service_info is undefined;
in second file :
this.select = function (connection,query,data){
connection.query(query,[data],function(err, results){
if(err)
{
throw err;
}
else
{
return results;
}
});}
You cannot just return values from callback due to call being asynchronous, you should use another function to get the results:
// file 'database.js'
exports.select = function (connection, query, data, callback) {
connection.query(query, [data], callback);
}
Than in your main:
// I assume `connection` and `service` are defined somewhere before (like in your original code)
var db = require("./database.js");
var service_info = db.select(connection,'select * from services where ivr_shortcode = ?',service, function(err, service_info){
console.log(service_info);
});
P.S.
You should really read some docs and look into Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference to get better understanding of scope visibility and closures
To deal with such condition you needs to implement it using the async/await.
exports.select = async function (connection,query,data){
await connection.query(query,[data],function(err, results){
if(err){
throw err;
}else{
return
}
});
}
This will waits until you get the result. So undefined condition never arise.
you have to export select.
something like this :
exports.select = function (connection,query,data){
connection.query(query,[data],function(err, results){
if(err)
{
throw err;
}
else
{
return
}
});}

Optimal design pattern - functions which process multiple files

Goal is to create distinct functions which separate out the work of loading multiple (xml) files and parsing them. I could do this all in one function, but the nested callbacks begin to get ugly. In other words, I don't want to do this:
// Explore directory
fs.readdir(path, function (err, files) {
if(err) throw err;
// touch each file
files.forEach(function(file) {
fs.readFile(path+file, function(err, data) {
if (err) throw err;
someAsyncFunction ( function (someAsyncFunctionResult) {
// Do some work, then call another async function...
nestedAsynchFunction ( function (nestedAsyncFunctionResult) {
// Do Final Work here, X levels deep. Ouch!
});
});
});
});
});
Instead, I want one function which reads my files and puts each file's XML payload into an array of objects which is returned to the caller (each object represents the name of the file and the XML in the file). Here's the function that might load up reports into an array:
function loadReports (callback) {
var path = "./downloaded-reports/";
var reports = [];
// There are TWO files in this path....
fs.readdir(path, function (err, files) {
if(err) throw err;
files.forEach(function(file) {
fs.readFile(path+file, function(err, data) {
if (err) throw err;
reports.push({ report: file, XML: data.toString()});
//gets called twice, which makes for strangeness in the calling function
callback(null, reports);
});
});
// callback won't work here, returns NULL reports b/c they haven't been processed yet
//callback(null, reports);
});
}
...and here's the function which will call the one above:
function parseReports() {
loadReports( function(err, data) {
console.log ("loadReports callback");
var reportXML = new jsxml.XML(data[0].XML);
var datasources = reportXML.child('datasources').child('datasource').child('connection').attribute("dbname").toString();
console.log(JSON.stringify(datasources,null, 2));
// More async about to be done below
} );
}
As you can see in the loadReports() comments, I can't get the callback to work right. It either calls back BEFORE the array is has been populated at all, or it calls back twice - once for each fs.readFile operation.
SO...what is the best way to deal with this sort of situation? In brief - What's the best design pattern for a function which processes multiple things asynchronously, so that it ONLY calls back when all "things" have been completely processed? The simpler the better. Do I need to use some sort of queuing module like Q or node-queue?
Thanks much!
Edit: Something like this works inside the deepest loop in terms of not hitting the callback twice, but it seems like a kludge:
fs.readdir(path, function (err, files) {
if(err) throw err;
files.forEach(function(file) {
fs.readFile(path+file, function(err, data) {
if (err) throw err;
reports.push({ report: file, XML: data.toString()});
// WORKS, but seems hacky.
if (reports.length = files.length) callback(null, reports);
});
});
});

Resources