Node.js: Async fs.writeFile queue is creating race condition? - javascript

I am trying to use async with node.js to handle multiple incoming POST requests to edit a JSON file. No matter how I refactor it, it will always make one of the edits and not the other. I though that using async.queue would force the operations to handle sequentially? What am I doing wrong?
My code:
var editHandler = function(task, done) {
var req = task.req;
var res = task.res;
fs.stat( "./app//public/json/" + "data.json", function(err, stat) {
if(err == null) {
console.log('File exists');
} else if(err.code == 'ENOENT') {
console.log("Error");
} else {
console.log('Some other error: ', err.code);
}
});
console.log(req.params.id);
console.log(req.body);
fs.readFile( "./app//public/json/" + "data.json", 'utf8', function (err, data) {
data = JSON.parse( data );
data[req.params.id] = req.body.school;
//console.log( data );
fs.writeFile("./app//public/json/" + "data.json", JSON.stringify(data), function (err){
if(err) {
return console.log(err);
}
})
res.redirect('/');
});
};
//Make a queue for the services
var serviceQ = async.queue(editHandler, 20);
serviceQ.drain = function() {
console.log('all services have been processed');
}
app.post('/edit_school/:id', function(req, res) {
serviceQ.push({req: req, res: res })
})
Thanks in advance for any insights! I am really new to using node.js for anything other than npm/webpack.

Related

Cannot set headers after they are sent to client Expressjs router

I'm getting error cannot set headers on express js, I think the problem is have to write setHeader, i was set but stil can't, this my code:
router.get('/cek', (req, res) => {
const child = execFile(commandd, ['-c', 'config', 'GSM.Radio.C0']);
child.stdout.on('data',
function (data) {
value = (JSON.stringify(data));
x = value.split('.');
y = JSON.stringify(x[2])
result = y.replace(/\D/g, "");
res.setHeader('Content-Type', 'text/html');
res.send(result);
}
);
child.stderr.on('data',
function (data) {
console.log('err data: ' + data);
}
);
});
I tired to fixing this error for two days, but still cannot, anybody can help?
As stated by Frederico Ibba, this is usually caused after res.send is sent and there is still data being processed... Your workaround for this may simply be to receive all the data before sending it out using res.send. You can try this.
async function executeCommand() {
return new Promise((resolve, reject) => {
const child = execFile(commandd, ['-c', 'config', 'GSM.Radio.C0']);
child.stdout.on('data',
function (data) {
value = (JSON.stringify(data));
x = value.split('.');
y = JSON.stringify(x[2])
result = y.replace(/\D/g, "");
resolve(result);
}
);
child.stderr.on('data',
function (err) { // Renamed data for err for clarification
reject(err);
}
);
});
}
router.get('/url', async (req, res) => {
try {
const result = await executeCommand();
res.setHeader('Content-Type', 'text/html');
res.send(result);
} catch(error) {
// There was an error. I'm throwing a 500
res.sendStatus(500);
}
});
Note that this will be effective only if you are confident that the data is being fired once, as indicated by skirtle

How to turn a query result into a js variable (Javascript)

I've been searching for this for a day now and I'm basically hopeless.
All I want to do is export the query result as a string (so dataString basically) so I can import as a string in my external .js file.
module.exports.getKlanten = function(req, res){
console.log("zoekt naar klanten");
pool.connect(function(err, client, done){
if(err){
return console.error('error fetching client from pool', err);
}
client.query("select * from abc.relations limit 5", function(err,result){
done();
if(err){
return console.error('error running query', err);
}
var dataString = JSON.stringify(result.rows);
var count = Object.keys(result.rows).length;
var klanten = result.rows;
res
.status(200)
.render("index", {dataString: dataString, klant: klanten, count: count});
console.log("done");
})
});
}
And what would I have to do in the js file to import the string then? It looks so easy yet I can't seem to get it right.
It would be something like this
module.exports.getKlanten = function(req, res){
console.log("zoekt naar klanten");
return "Hello world"; }
and then in your external .js file, you can import it like this
const myModule = require('./JSFile');
and use it like this
console.log(myModule.getKlanten());
Also, use a return statement so that you have a string in your variable.
I am assuming that you need to use the same function externally somewhere else and also for http handler so split it into three files.
//getKlanten.js
module.exports.getKlanten = function(){
return new Promise(function (resolve, reject) {
pool.connect(function(err, client, done){//make sure pool is avialble here
if(err){
console.error('error fetching client from pool', err);
reject(err);
}
client.query("select * from abc.relations limit 5", function(err,result){
if(err){
console.error('error running query', err);
reject(error);
}
var dataString = JSON.stringify(result.rows);
var count = Object.keys(result.rows).length;
var klanten = result.rows;
var data = {dataString: dataString, klant: klanten, count: count}
resolve(data);
})
});
})
}
//in external.JS
var getKlanten = require('getKlanten');
getKlanten().then(function(object) {
console.log(object);
}, function(err){
console.log(err);
})
//in http handler file
var getKlanten = require('getKlanten');
module.exports = function(req,res) {
getKlanten().then(function(data) {
res
.status(200)
.render("index", data);
});
}

async.each cannot set headers after already set

Here's what happening. I'm saving new companies first, then attaching the _id to each new user before they get saved. The issue I'm running into is returning a response. When I put the res.json() into the function thats getting repeated obviously I'm getting an error because I already have a response sent from the first time it loops through.
So, How do I call signupSeq(record, res) but wait for the async methods to finish so I know whether I have an error or not?
var signupSeq = function(req, res) {
async.waterfall([
function(callback) {
console.log(req);
if (req.company._id===undefined){
var company = new Company(req.company);
company.save(function(err){
if (err) {
console.log('save error');
callback(err);
}else{
callback(null, company._id);
}
})
}else{
callback(null, req.company._id); //pass teh plain ID if it's not a new name:xxx
}
},
function(companyId, callback) {
delete req.company
req.company = companyId
// Init Variables
var user = new User(req);
var message = null;
// Add missing user fields
user.provider = 'local';
user.displayName = user.firstName + ' ' + user.lastName;
// Then save the user
user.save(function(err) {
if (err) {
callback(err);
} else {
callback(null, user);
}
});
}
], function (err, result) {
if(err){
console.log(result+'funciton result')
return err
// res.status(400).send({
// message: errorHandler.getErrorMessage(err)
// });
}else{
console.log(result+'funciton result')
return result
//res.json(result)
}
});
}
exports.saveMany = function(req, res){
async.each(req.body, function(record, callback) {
// Perform operation on record.body here.
console.log('Processing record.body ' + record);
// Do work to process record.body here
var x = signupSeq(record, res)
console.log(x+'<<<<<<<value of x');
console.log('record.body processed');
callback();
}, function(err){
// if any of the record.body processing produced an error, err would equal that error
if( err ) {
res.json(err);
// One of the iterations produced an error.
// All processing will now stop.
console.log('A record.body failed to process');
} else {
res.json('Success');
console.log('All files have been processed successfully');
}
});
}
You could add a callback (cb) in your signupSeg function.
var signupSeq = function(req, res, cb) {
async.waterfall([
function(callback) {
console.log(req);
if (req.company._id===undefined){
var company = new Company(req.company);
company.save(function(err){
if (err) {
console.log('save error');
callback(err);
}else{
callback(null, company._id);
}
})
}else{
callback(null, req.company._id); //pass teh plain ID if it's not a new name:xxx
}
},
function(companyId, callback) {
delete req.company
req.company = companyId
// Init Variables
var user = new User(req);
var message = null;
// Add missing user fields
user.provider = 'local';
user.displayName = user.firstName + ' ' + user.lastName;
// Then save the user
user.save(function(err) {
if (err) {
callback(err);
} else {
callback(null, user);
}
});
}
], function (err, result) {
if(err){
console.log(result+'funciton result')
cb(err)
// res.status(400).send({
// message: errorHandler.getErrorMessage(err)
// });
}else{
console.log(result+'funciton result')
cb(null,result)
//res.json(result)
}
});
}
exports.saveMany = function(req, res){
async.each(req.body, function(record, callback) {
// Perform operation on record.body here.
console.log('Processing record.body ' + record);
// Do work to process record.body here
signupSeq(record, res,function(err,result){
var x= result;
console.log(x+'<<<<<<<value of x');
console.log('record.body processed');
callback();
})
}, function(err){
// if any of the record.body processing produced an error, err would equal that error
if( err ) {
res.json(err);
// One of the iterations produced an error.
// All processing will now stop.
console.log('A record.body failed to process');
} else {
res.json('Success');
console.log('All files have been processed successfully');
}
});
}
This way inside the asyn.each the signipSeg will have to finish before the call of the callback().
Hope this helps.

Node.js npm mssql function returning undefined

I am using mssql with node.js to connect to an sql server db. I am trying to reduce code by wrapping the connection code in a function with one query parameter. When I call the function from with in a router.get function, it returns undefined.
Any help would be much appreciated.
function sqlCall(query) {
var connection = new sql.Connection(config, function(err) {
if (err) {
console.log("error1");
return;
}
var request = new sql.Request(connection); // or: var request = connection.request();
request.query(query, function(err, recordset) {
if (err) {
console.log("error2");
return;
}
return (recordset);
});
});
}
router code
router.get('/', function(req, res) {
var queryString = "select * from .....";
res.json(sqlCall(queryString));
//sqlCall(queryString)
});
You are trying to treat the sqlCall as a synchronous function with a return value, while the request.query function on the opposite is an asynchronous function, expecting a callback.
Since Node.js uses non blocking IO and callback structures for flow control, using an asynchronous structure based around callbacks is the way to go. In your case this could look like this:
router.get('/', function(req, res) {
var queryString = "selec * from .....";
sqlCall(queryString, function(err, data) {
if (typeof err !== "undefined" && err !== null) {
res.status(500).send({
error: err
});
return;
}
res.json(data);
});
});
with your other component looking like this:
function sqlCall(query, cb) {
var connection = new sql.Connection(config, function(err) {
if (typeof err !== "undefined" && err !== null) {
cb( err );
return
}
var request = new sql.Request(connection); // or: var request = connection.request();
request.query(query, function(err, recordset) {
cb( err, recordset );
});
});
}

How to make a GET and POST request to an external API?

var Attendance = require('../../../collections/attendance').Attendance;
var moment = require('moment');
module.exports = function(app) {
app.get('/api/trackmyclass/attendance', function(req, res) {
var data = req.body;
data['user'] = req.user;
Attendance.getByUser(data, function(err, d) {
if (err) {
console.log('This is the err' + err.message);
res.json(err, 400);
} else {
var job = d['attendance'];
if (typeof job != undefined) {
res.json(job);
console.log('This is it' + job['status']);
} else
res.json('No data Present', 200);
}
});
});
app.post('/api/trackmyclass/attendance', function(req, res) {
var data = req.body;
data['user'] = req.user;
Attendance.create(data, function(err, d) {
if (err) {
console.log('This is the err' + err.message);
res.json(err, 400);
} else {
var attendance = d['attendance'];
if (typeof job != undefined) {
console.log('Attendance record created' + attendance);
res.json(attendance);
} else
res.json('No data Present', 200);
}
});
});
}
This is the api code I to which I need to make the GET and POST request. But I have no idea how to do it.
It looks like your code is using express which would normally be good for building and API for your app. However to make a simple request to a third party api and staying in node.js why not try the request module which is great. https://www.npmjs.org/package/request
Your example does not show what the path of the request is or if you need any additinal headers etc but here is a simple example of a GET request using request.
var request = require('request');
function makeCall (callback) {
// here we make a call using request module
request.get(
{ uri: 'THEPATHAND ENDPOINT YOU REQUEST,
json: true,
headers: {
'Content-Type' : 'application/x-www-form-urlencoded',
}
},
function (error, res, object) {
if (error) { return callback(error); }
if (res.statusCode != 200 ) {
return callback('statusCode');
}
callback(null, object);
}
);
}
or jquery .ajax from a front end client direcct to your path
$.ajax({
url: "pathtoyourdata",
type: "GET",
})
.done(function (data) {
//stuff with your data
});

Categories

Resources