Node.JS module.exports for passing parameter between two functions? - javascript

I am new to Node.js so this is probably a fundamental question and I am missing something obvious. In the code below, I am trying to set the sql_file name from foo but I keep getting an error on the file not existing because the variable is not working. If I hard code the file name in sql_util.js it works fine.
So how do I pass a parameter or any object from one js file into the function of another?
foo.js
var misc = require('./sql_get');
console.log(misc.sql_file("on.sql"));
sql_util.js
fs = require('fs');
file = 'on.sql'
function sql_file(cb) {
var fileName = "./SQLs/" + sql_file;
fs.readFile(fileName, function(err, buffer) {
if (err) return cb(err);
return cb(null, buffer.toString());
});
}
sql_file(function(err, sqlstatement) {
if (err) throw err;
console.log('sql statement is: ' + sqlstatement);
});
};
module.exports.x = x;
module.exports.sql_fil = sql_file;

Let's go through this line by line because I see a lot of errors, both syntax and semantic.
foo.js
var misc = require('./sql_get');
console.log(misc.sql_file("on.sql"));
You defined in the function below sql_file to be asynchronous, so it does not return a value, but takes a callback that it passes the result to.
sql_util.js
fs = require('fs');
file = 'on.sql'
You have an unused module-global variable file. Remove this. It's going to cause confusion.
function sql_file(cb) {
var fileName = "./SQLs/" + sql_file;
sql_file is a function. I can tell because this code lives in a function called sql_file. Therefore, fileName will be "./SQLs/" + the .toString() result of the function, which is the source of the function. I think perhaps you want a parameter?
fs.readFile(fileName, function(err, buffer) {
if (err) return cb(err);
return cb(null, buffer.toString());
});
}
This seems ok.
sql_file(function(err, sqlstatement) {
if (err) throw err;
console.log('sql statement is: ' + sqlstatement);
});
};
I am not sure what you are trying to do here. Why are you calling the function?
module.exports.x = x;
module.exports.sql_fil = sql_file;
Both of these lines have a problem. There is no symbol defined named x. I am surprised this code runs without throwing an error on that line. Second, you are exporting sql_file as sql_fil, missing the letter e.
The Solution
Since what you asked about is parameter passing and you only have a single function, let's simplify this. You never want to use global variables. If a function needs a particular variable, just pass it as an argument.
foo.js
var misc = require('./sql_get');
misc.sql_file('on.sql', function (err, contents) {
console.log(contents);
});
sql_get.js (notice the file is renamed)
var fs = require('fs');
function sql_file(sqlFilename, cb) {
var fileName = "./SQLs/" + sqlFilename;
fs.readFile(fileName, function(err, buffer) {
if (err) return cb(err);
return cb(null, buffer.toString());
});
}
module.exports.sql_file = sql_file;

A few problems:
You're requiring sql_get but naming the other file sql_util
var misc = require('./sql_util');
You're exporting module.exports.sql_fil = sql_file; (see the missing e). You probably mean;
module.exports.sql_file = sql_file;
While calling sql_file, you are passing a string but expecting a cb in the function itself -
misc.sql_file("on.sql", function(err, fileContent) {
if(err) return console.log(err);
console.log('File content: ', fileContent);
});
function sql_file(sqlFileName, cb) {
var fileName = "./SQLs/" + sqlFileName;
fs.readFile(fileName, function(err, buffer) {
if (err) return cb(err);
return cb(null, buffer.toString());
});
}
And I don't know what you are doing with calling sql_file function in that file itself. Remove that.

Related

Value update after finish job works in text file with Node.js

I have my text data like this and I mark * charset to check unfinished job.
And here's my part of code.
I want to make it when txt lines are start with * charset, it should be processed and after that processed string's * charset should be removed.
fs.readFile('data.txt', async function (err, data) {
if (err) throw err;
let array = data.toString().split("\n");
for (i in array) {
if (array[i].charAt(0) === '*') {
console.log(`Now Processing : ${array[i]} | ${array.length - i -1} items left`);
//
// SOME JOBS
//
let newValue = array[i].replace('*', '');
fs.writeFile('data.txt', newValue, 'utf-8', function (err, data) {
if (err) throw err;
console.log('Done!');
})
} else {
console.log(`${array[i]} Already Captured`)
}
From what I understand you are trying to read through a file, find lines containing "*" character, doing some work, then removing the * from the affected lines.
Firstly, the call to fs.writeFile is happening inside a loop, so every iteration of that loop calls the writeFile function. From the nodejs docs this method will "... asynchronously write data to the file, replacing the file if it already exists." You are replacing the file with every iteration. What you want to do is use fs.appendFile or better yet pass the 'append' system flag to writeFile. System flags can be seen here. Take a look at the 'a' flag and pass it in the options object of writeFile.
Your usage of async in the readFile callback is incomplete also as you don't call await within that callback.
fs.readFile('data.txt', async function (err, data) {
if (err) throw err;
let array = data.toString().split("\n");
for (i in array) {
if (array[i].charAt(0) === '*') {
console.log(`Now Processing : ${array[i]} | ${array.length - i -1} items left`);
//
// SOME JOBS
//
let newValue = array[i].replace('*', '');
newValue = newValue + '\n';
await fs.appendFile('data.txt', newValue, 'utf-8', function (err, data) {
if (err) throw err;
console.log('Done!');
})
} else {
console.log(`${array[i]} Already Captured`)
}
}
});

better way of writing nodejs function of readfile/write file

How can i write this code in a better way.
var fs = require('fs');
var file = '/test.txt';
fs.readFile(file, 'utf8', function (err, txt) {
if (err) return console.log(err);
txt = txt + '\nAppended something!';
fs.writeFile(myFile, txt, function (err) {
if(err) return console.log(err);
console.log('Appended text!');
});
});
Suppose i have multiple callback then how can we prevent this callback of callback and so on....
getData(function(a){
getMoreData(a, function(b){
getMoreData(b, function(c){
getMoreData(c, function(d){
getMoreData(d, function(e){
...
});
});
});
});
});
I really like bluebird for this:
First you have to 'promisify' fs. n the example below they directly promisify the readFile method:
var readFile = Promise.promisify(require("fs").readFile);
readFile("myfile.js", "utf8").then(function(contents) {
return eval(contents);
}).then(function(result) {
console.log("The result of evaluating myfile.js", result);
}).catch(SyntaxError, function(e) {
console.log("File had syntax error", e);
//Catch any other error
}).catch(function(e) {
console.log("Error reading file", e);
});
or:
var fs = Promise.promisifyAll(require("fs"));
// note now you have to put 'async' after the methods like so:
fs.readFileAsync("myfile.js", "utf8").then(function(contents) {
console.log(contents);
}).catch(function(e) {
console.error(e.stack);
});
I suggest async waterfall
Your first snippet would look like following:
var txt;
async.waterfall([
function(callback) {
fs.readFile(file, 'utf8', callback);
},
function(txt, callback) {
txt = txt + '\nAppended something!';
fs.writeFile(myFile, txt, callback);
},
function(callback) {
console.log('Appended text!');
callback();
}
], function (err, result) {
console.log(err)
});
What you're describing is callback hell and there's a couple of smart ways to get around it. I don't claim to be the know it all but there's a whole website called callbackhell.com that you might want to check out.
But for the short answer, you can do these things
1. keep your code shallow, or name your functions
Instead of writing your fs.readFile with an anonymous function, name it and call it like so
fs.readFile(file, 'utf8', function readFileCb(err, txt) {
if (err) throw new Error(err);
txt = txt + '\nAppended something!';
fs.writeFile(myFile, txt, function (err) {
// no need to return a console.log, just throw Error should suffice
if(err) throw new Error(err);
console.log('Appended text!');
});
});
2. Modularize your code. Have named functions or libraries that do exactly one thing
function writeFile(file, txt, cb){
fs.writeFile(file, txt, cb)
}
function writeFileCb(err){
if(err) throw new Error(err);
console.log('Appended Text!');
}
fs.readFile(file, 'utf8', function readFileCb(err, txt) {
if (err) throw new Error(err);
txt = txt + '\nAppended something!';
writeFile(myFile, txt, writeFileCb);
});
3. Ensure that all errors are caught. You seem to be doing that well, so kudos!
You can also use Promises, libraries like Async waterfall, but callbacks are an essential parts of JavaScript and going through callback hell is just a matter of having good sense in writing your code.

Modules with Arguments NodeJS

I have two files, home.js and module.js in the same directory.
What I'm trying to do is, I'm trying to pass the variable named directory as I call the function I exported from module.js.
It gives me this error:
binding.readdir(pathModule._makeLong(path), req);
Type error: path must be a string.
What I'm trying to figure out is, I've passed the directory variable which is process.argv[2] (contains the path) from home.js as I call the function in module.js that requires the same argument (path).
home.js
var fs = require('fs');
var path = require('path');
var module = require('./module.js');
var directory = process.argv[2];
var extensionRequired = process.argv[3];
function printList(err, data) {
if(err) return err;
list.forEach(function (file) {
if(path.extname(file) === '.' + extensionRequired) {
console.log(file);
}
});
}
module(directory, extensionRequired, printList);
module.js
var fs = require('fs');
var path = require('path');
module.exports = function (directory, extensionRequired, callBack) {
fs.readdir(directory, function(err, list) {
if(err) return err;
callBack(err, list)
});
}
I think you made a mistake, and forgot to rename the list variable:
function printList(err, data) {
if(err) return err;
// Here list => data
data.forEach(function (file) {
if(path.extname(file) === '.' + extensionRequired) {
console.log(file);
}
});
}
In your callback-method, named printList, you set the second argument as data. If you want to access the second argument's value again, you have to use data in your code or reassign it to another variable.
Your method may then look like this:
function printList(err, data) {
if (err) return err;
data.forEach(function (file) {
if(path.extname(file) === '.' + extensionRequired) {
console.log(file);
}
});
}
Additionally, I see two more problems with your code:
In module.js, you're requiring the parameter extensionRequired. If you look closely, you'll find, that it isn't even used in this method. This isn't really an error, but would in my opinion be seen as inelegant. Rather pass it trough to the printList as an additional argument (more the node-typical way IMHO) or use it as a global-scope variable as you are currently doing anyway.
In your module.exports-anonymous function from module.js, you are using if (err) return err;. I'd highly recommend you to not do such a thing. Because this is an asynchronous method, you can't really return something, as the return-statement might actually be executed after you called this method. Instead, pass your error as the first argument of the callback. If there is no error, pass null instead, so you can easily figure out if something unexpected happened. Always check that!
Your module.js could then look something like this:
var fs = require('fs');
var path = require('path');
module.exports = function (directory, callback) {
fs.readdir(directory, function(err, list) {
if (err)
// Error happened, pass it to the callback
callback(err);
else
// Everything ran smooth, send null as the error (no error)
// and the list as the second argument.
callback(null, list)
});
}
Your home.js should then be changed accordingly:
var fs = require('fs');
var path = require('path');
var module = require('./module.js');
var directory = process.argv[2];
var extensionRequired = process.argv[3];
function printList(err, data) {
if (err) {
console.error("An error occurred:", err);
// Exit with an error-code of one to
// tell failure to the calling process and
// prevent printing the probably 'undefined' data-variable
process.exit(1);
}
data.forEach(function (file) {
if(path.extname(file) === '.' + extensionRequired) {
console.log(file);
}
});
}
// extensionRequired removed, as it is not needed
module(directory, printList);

NodeJS - Given a full path of a directory, how to get a list of all subdirectories and check if a file exists here asynchronously?

For example given: '/Users/John/Desktop/FooApp',
I would like to get a list such as:
['/Users/John/Desktop/FooApp',
'/Users/John/Desktop/FooApp/Folder1',
'/Users/John/Desktop/FooApp/Folder2',
'/Users/John/Desktop/FooApp/Folder2/folderA',
'/Users/John/Desktop/FooApp/Folder3',
'/Users/John/Desktop/FooApp/Folder3/folderX',
'/Users/John/Desktop/FooApp/Folder3/folderX/folderY',
'/Users/John/Desktop/FooApp/Folder3/folderX/folderY/folderZ',
'/Users/John/Desktop/FooApp/Folder3/folderX/folderY2'
]
I require this list to search through all directories to check the existence of a file. The user inputs a folder, and I basically will perform a check similar to finders in OS. I am planning to check fs.exists(subdir + '/mylib.dll') on all the subdirectories. Is there any neat way to do that?
I have converted an answer to a similar question in here, where search was performed for files instead of directories. I also used async to check if file exists. I also found out that fs.exists was about to be deprecated and decided to go on with fs.open.
Anyway, Here is the snippet:
var fs = require('fs');
var getDirs = function(dir, cb){
var dirs = [dir];
fs.readdir(dir, function(err, list){
if(err) return cb(err);
var pending = list.length;
if(!pending) return cb(null, dirs);
list.forEach(function(subpath){
var subdir = dir + '/' + subpath;
fs.stat(subdir, function(err, stat){
if(err) return cb(err);
if(stat && stat.isDirectory()){
dirs.push(subdir);
getDirs(subdir, function(err, res){
dirs = dirs.concat(res);
if(!--pending) cb(null, dirs);
});
} else {
if(!--pending) cb(null, dirs);
}
});
});
});
};
One can then use it as:
var async = require('async');
getDirs('/Users/John/Desktop/FooApp', function(err, list){
if(err) return 'Error retrieving sub-directories';
async.detect(list, function(dir, cb){
fs.open(dir + '/mylib.dll', 'r', function(err, file){
if(err) cb(false);
else cb(true);
});
},
function(dir) {
if(!dir) return 'File Not Found';
/* Do something with the found file ... */
}
);
});

Returned object undefined from module function

This has to be a scope issue that I'm not familiar with. I have a small module I've written as so:
(function () {
var getPlanInfo = function (id, conn) {
conn.query('SELECT * FROM `items` WHERE `id` = ?', [id], function (error, result) {
if (error) console.error('Query error: ' + error.stack);
console.log(result[0]); // Everything is great
return result[0];
});
};
modules.exports.getPlanInfo = function (id, conn) { return getPlanInfo(id, conn); // Typo }
})();
Here comes the problem. When I call it from anywhere (inside the module itself or another file), the return value is always undefined. I checked from within the function, the query returns the result as expected.
var backend = require('./module.js');
var t = backend.getPlanInfo();
t is undefined. This is the same if I call that method from inside the module itself (another function within that module).
I'm familiar with the callback principle in javascript and how objects and functions have to be passed around as an argument to remain in scope. Is this the issue here or is this a node.js particularity?
I tried in in the Developer Console (Chrome), works as expected.
conn.query() looks like it is async. Thus, you can't return its result from getPlanInfo() because getPlanInfo() returns long before the result is available. Returning result[0] from the conn.query() callback just returns a piece of data back into the conn.query() infrastructure. getPlanInfo() has long before already returned.
If you want an async result, then you will have to change getPlanInfo() to use a mechanism that supports getting async results such as a direct callback or a promise or something like that.
Here's a plain callback way:
var getPlanInfo = function (id, conn, callback) {
conn.query('SELECT * FROM `items` WHERE `id` = ?', [id], function (error, result) {
if (error) {
console.error('Query error: ' + error.stack);
callback(error);
return;
}
console.log(result[0]); // Everything is great
callback(0, result[0]);
});
};
modules.exports.getPlanInfo = getPlanInfo;
Then, the caller of that module would look like this:
var m = require('whatever');
m.getPlanInfo(id, conn, function(err, result) {
if (err) {
// error here
} else {
// process result here
}
});
You don't return anything from getPlanInfo. Probably you wanted to write modules.exports.getPlanInfo = function (id, conn) { return getPlanInfo; }
(with return getPlanInfo; instead of return getPlanInfo();)

Categories

Resources