I am a newbie in Node JS and trying to get my head around functional programming.
I have the following code in a file called findinfo.js and I'm trying to pass the result to the main server.js:
const fs = require('fs');
const values = ["yes", "no", "?", "unknown", "partial"];
var cInfo = [];
function getFile (cb) {
fs.readFile('./scripts/blahblah.json', 'utf-8', function (err, jfile) {
if (err) {
throw new Error (err);
}
console.log("Function is executing...")
JSON.parse(jfile);
console.log('Parsing file done');
cb(jfile);
});
}
Then I'm trying to call this function from server.js,
var findinfo = require('./findinfo');
console.log(getFile());
which as expected crashes the program.
So what changes should I make in order to make it work?
You need to export getFile so it can be imported using require.
const fs = require('fs');
const values = ["yes", "no", "?", "unknown", "partial"];
var cInfo = [];
function getFile (cb) {
fs.readFile('./scripts/blahblah.json', 'utf-8', function (err, jfile) {
if (err) {
// throw new Error (err); // don't throw inside async callback
return cb(err);
}
console.log("Function is executing...")
JSON.parse(jfile);
console.log('Parsing file done');
cb(null, jfile);
});
}
module.exports = getFile;
server.js
var getFile = require('./findinfo');
getFile(function(err, file) {
console.log(err, file);
});
since getFile is an asynchronous function, you have to wait until it finishes, when cb is called, to console.log the result.
And using throw in a asynchronous callback is not recommended, since it will crash the server, it's recommended to pass the error to the callback.
You should take a look at this question, so you learn more about how to handle asynchronous code.
How do I return the response from an asynchronous call?
Related
I think the rendering takes place before the searching of the string on the files, i have tried different methods but don't seems to get this working. any help will be appreciated. im a noob on to the nodejs. im trying to get the id of the user and query and get all the data and there after see if he is in any of the lists given and finally render the page.
const j = [];
let name = '';
const filename = [];
var ext = '';
module.exports = function(app, express) {
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.post('/cusdetails', isLoggedIn, function (req, res) {
var cusid=req.body.cusid;
var insertQuerys = "SELECT * FROM customer WHERE cusid=? ORDER BY rowid DESC LIMIT 1";
connection.query(insertQuerys,[cusid],
function(err, rows){
rows.forEach( (row) => {
name=row.fncus;
});
fs.readdir('./views/iplist', function(err, files) {
if (err)
throw err;
for (var index in files) {
j.push(files[index])
}
j.forEach(function(value) {
var k = require('path').resolve(__dirname, '../views/iplist/',value);
fs.exists(k, function(fileok){
if(fileok) {
fs.readFile(k, function(err, content) {
if (err) throw err;
if (content.indexOf(name) > -1) {
ext = path.extname(k);
filename.push(path.basename(k, ext));
}
});
}
else {
console.log(" FileNotExist ");
}
});
});
});
console.log(filename);
res.render('cusdetails.ejs', {rows: rows, user:req.user , aml: filename });
});
})
You can create simple Promise wrapper and then use it inside async/await function to pause execution until resolved.
// use mysql2 package as it provides promise, less work to write promise wrappers
const mysql = require('mysql2/promise');
// create the connection to database
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
database: 'test'
});
// sample wrapper
function some(k) {
// more advisable to have local variables, why do you need this to be array?
var filename = [];
return new Promise((resolve, reject) => {
// doing this is also not recommended check nodejs documentation **fs.exists** for more info
fs.exists(k, function(fileok){
if(fileok) {
fs.readFile(k, function(err, content) {
if (err) reject(err);
if (content.indexOf(name) > -1) {
ext = path.extname(k);
filename.push(path.basename(k, ext));
resolve(filename)
}
});
}
else {
// reject(new Error("FileNotExist"))
console.log(" FileNotExist ");
}
});
})
}
// note the use of async
app.post('/cusdetails', isLoggedIn, async function (req, res) {
var cusid=req.body.cusid;
var insertQuerys = "SELECT * FROM customer WHERE cusid=? ORDER BY rowid DESC LIMIT 1";
// using await to pause excution, waits till query is finished
const [rows] = await connection.query(insertQuerys,[cusid])
rows.forEach( (row) => {
name=row.fncus;
});
// then you can
var result = await some(k)
...
Note however this way you loose the advantage of concurrent execution, as it's kindoff blocking. If the result of one call is not used in another, you can execute in parallel and await for result to achieve sequencing like
const [rows] = connection.query(insertQuerys,[cusid])
var result = some(k)
console.log(await rows) // do something
console.log(await result) // do something
JavaScript is asynchronous. This means that if you have a function with a callback (i.e. your query), the callback will be called asynchronously, at an unknown time, while the other code executes.
You need to look up some tutorials how to deal with callbacks, to get a proper understanding of it. Another method is using async/await and/or promises.
Basically, if you take the following code:
console.log("this will print first");
setTimeout(function () {
console.log("this will print last");
}, 1000);
console.log("this will print second");
If you run the code above, the top level is executed synchronously, so, it first calls console.log, then it executes setTimeout, which is synchronous. It sets a timeout, then says "I'm ready", and the code continues to the other console.log. After 1 second (1000 milliseconds), the callback in the setTimeout function is executed, and only then that console.log is called. You can not make the rest of the code wait this way, you need to restructure your code or read into promises.
Background
I have taken some sample data from a large XML response I will be working with in my app. I decided to save the xml to a response.xml file in my app so I can parse the data without making a bunch of unnecessary request to the api.
I am going to be using xml2js to convert the xml response to a JS Object.
Problem
I have been able to open the file and console.log() it.
I have been able to run xml2js by passing a small xml string to it which I also console.log() it.
But I have only been able to console.log() the file after xml2js creates the object. No matter what I try I continue to get null when using return or passing trying to pass the data outside of the initial creation of the object.
Example
This prints xml tree to the console,
var fs = require('fs');
var openFile = fs.readFile('./response.xml', 'utf8', function (err,data) {
if (err) {
return console.log(err);
}
console.log(data);
});
function requestCreditReport() {
return openFile;
}
requestCreditReport();
This prints a small xml string to console statically added in with
xml2js,
var parseString = require('xml2js').parseString;
var xml = "<root>Hello xml2js!</root>";
parseString(xml, function (err, result) {
console.dir(result);
});
Question
How do I use the object once it is created outside of the method below. Initially when it is created I can output it to the console but cannot seem to access it outside of that console.log(). in the example below I am trying return result. This leaves the value null when I try to pass the object returned by the function to a variable like this,
var response = requestReport();
Recent try,
var fs = require('fs');
var parseString = require('xml2js').parseString;
function requestReport() {
fs.readFile('./response.xml', 'utf8', function (err,data) {
if (err) {
return console.log(err);
}
return parseString(data, function (err, result) {
return result;
});
});
}
var response = requestReport();
console.log(response);
This returns null. But if I console.log(result) instead or using return then trying outside of the function it returns this,
{ RESPONSE_DATA:
{ '$': { MYAPI: '0.0.0' },
DETAILS: [ [Object] ],
DETAILS_ACCOUNT: [ [Object] ],
RESPONSE: [ [Object] ] } }
requestReport is async. It doesn't return anything so response is undefined.
You have to use a callback.
var fs = require('fs');
var parseString = require('xml2js').parseString;
function requestReport(callback) {
fs.readFile('./response.xml', 'utf8', function(err, data) {
if (err) return callback(err);
parseString(data, callback);
});
}
requestReport(function(err, result) {
if (err) return console.error(err);
console.log(result);
});
I have this simple piece of code:
var http = require('http'), fs = require("fs");
function get(p) {
fs.readFile('.' + p,'utf8', function (err, cont) {
if (err) return "EERRRORRRRR";
else return cont;
})
}
http.createServer(function (request, response) {
var path = ((request.url==="/")?"/index.html":request.url);
console.log(get(path));
}).listen(80);
When I run and connect to the server, it logs undefined...
When I add a "console.log(cont)" like:
fs.readFile('.' + p,'utf8', function (err, cont) {
console.log(html)
if (err) return "EERRRORRRRR";
else return cont;
})
; it logs the correct contents, so why is the function returning undefined? the contents exists...
How would i fix this issue?
The originally context of the code was a simple web server, if you couldn't tell.
Read about callbacks and asynchronous functions, you can find docs in google
var http = require('http'),
fs = require("fs");
// notice new parameter callback
function get(p, callback) {
fs.readFile('.' + p,'utf8', callback);
}
http.createServer(function (request, response) {
var path = ((request.url==="/")?"/index.html":request.url);
// get accepts callback
get(path, function(err, data){
if(err){
response.send('not found');
} else {
response.send(data);
}
});
}).listen(80); // notice: port 80 requires sudo to run, use better 3000
readFile in node.js is async (as well as almost all other functions). You can't return values from async functions, instead, you need to use a callback function that will be called once the operation ends:
fs.readFile('.' + p,'utf8', function (err, cont) {
console.log(html)
if (err) return "EERRRORRRRR";
else handleResponse(cont);
})
function handleResponse(data){//Do something here}
Use readFileSync if you want to return something without having to use a callback.
function get(p) {
var file = fs.readFileSync('.' + p,'utf8');
return file ? file : "EERRRORRRRR";
}
This assumes you don't mind using synchronous/blocking code.
I have a main in nodejs for my program where I need to use my result calculated in a module, but my I don't have the right result.
var myJSONClient = {
"nombre" : "<nombre_cliente>",
"intervalo" : [0,0]
};
var intervalo = gestionar.gestion(myJSONClient,vector_intervalo);
console.log("intervalo: "+intervalo); //return undefined
And this is the module
var gestion = function(myJSON,vector_intervalo) {
var dburl = 'localhost/mongoapp';
var collection = ['clientes'];
var db = require('mongojs').connect(dburl, collection );
var intervalo_final;
function cliente(nombre, intervalo){
this.nombre = nombre;
this.intervalo = intervalo;
}
var cliente1 = new cliente(myJSON.nombre,myJSON.intervalo);
db.clientes.save(cliente1, function(err, saveCliente){
if (err || !saveCliente) console.log("Client "+cliente1.nombre+" not saved Error: "+err);
else {
console.log("Client "+saveCliente.nombre+" saved");
intervalo_final = calculate(vector_intervalo);
console.log(intervalo_final); //here I can see the right content of the variable intervalo_final
}
});
setTimeout(function(){
console.log("pause");
},3000);
console.log(intervalo_final); //result not correct
return intervalo_final;
}
exports.gestion = gestion;
I know that node execute my return without wait the end of my function, for this I can't see the right result, but how can I force my program to wait the end of my function?
I tried with the setTimeout function but wasn't the right way.
You must implement your function just like the other async functions from the API!
First step : give callback to function
var gestion = function(myJSON,vector_intervalo, callback) {
Second step : when the async process is over call callback passing the result (you don't need the return line)
console.log(intervalo_final); //here I can see...
callback(intervalo_final);
Step three: use your function in an async way
gestionar.gestion(myJSONClient,vector_intervalo, function(result){
console.log(result);
});
In async JS you can't return a value the way it seems you trying to do. You need to pass a callback function from your main program when calling gestionar.gestion() (you can add it as a third argument).
Your code sample won't work because function gestion() returns immediately, before intervalo_final content is set.
Something like this:
gestionar.gestion(myJSONClient,vector_intervalo, function callback(intervalo) {
// This is the callback function
console.log("intervalo: " + intervalo);
});
And then within the function:
var gestion = function(myJSON,vector_intervalo, callback) {
...
db.clientes.save(cliente1, function(err, saveCliente) {
if (err || !saveCliente) {
console.log("Client "+cliente1.nombre+" not saved Error: "+err);
if (callback) callback(); // execute callback function without arguments
}
else {
console.log("Client "+saveCliente.nombre+" saved");
intervalo_final = calculate(vector_intervalo);
console.log(intervalo_final);
if (callback) callback(intervalo_final); // your callback function will be executed with intervalo_final as argument
}
});
Also, I highly recommend reading some async javascript tutorial, like http://javascriptissexy.com/understand-javascript-callback-functions-and-use-them/
And Felix's Node.js Guide: http://nodeguide.com/
I want to pass a stream as argument to a function and use it in an async callback but it is destroyed (stream.readable is false)
for example:
var test = require('./test');
var file = fs.createReadStream('./file.txt');
test(file, console.log);
and in test.js:
module.exports = function(stream, callback) {
//stream.pipe(process.stdout); ///////// STREAM IS READABLE HERE
doSomething('abc', function(err) {
stream.pipe(process.stdout); ///////// STREAM IS NOT READABLE HERE
callback(err);
});
};
enter code here
why is this happening ?
what can I do to use it in the callback ?
This happens because stream ends before you trying to pipe it. In the first place stream is readable because you synchronous code still working. In the second place (inside of callback) stream already ended because callback may be executed after several ticks in future. You need to pasue your streem if you want to read it in future. This code should work:
var test = require('./test');
var file = fs.createReadStream('./file.txt');
file.pause();
test(file, console.log);
test.js
module.exports = function(stream, callback) {
doSomething('abc', function(err) {
stream.resume();
stream.pipe(process.stdout);
callback(err);
});
};