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.
Related
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.
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);
});
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
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
}
});}
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);
});
});
});