i know this question may be asked about 1000 times here but i cant find the error in my script.
I am trying to iterate through 2 arrays to get a name from an city_id and an organisation_id in a each() loop. i would like to write there values to the "mother"-object to get all informations in one place. Here is the code i've written so far:
let express = require('express');
let router = express.Router();
let request = require('request');
let connection = require('../lib/mysql');
router.get('/', function(req, res) {
if(req.session.loggedin === true){
getList((err, finlist) => {
console.log(finlist)
});
} else {
const cssPath = '/stylesheets/style.css'
res.render('login', { cssPath, title: 'Login' });
}
});
function getList(callback) {
var result = [];
connection.query('SELECT * FROM lists ', (err, rows) => {
if(err) throw err;
var r=0;
rows.forEach(function(item) {
result[r] = item;
getCities((err, cty) => {
result[r].city = cty[item.city_id].name;
getOrganisations((err, org) => {
result[r].organisation = org[item.organisation_id].name;
});
callback(result);
});
r++;
});
});
}
function getCities(callBack) {
var result=[];
connection.query('SELECT * FROM cities ', (err, rows) => {
if (err) throw err;
rows.forEach(function (cty) {
result[cty.id] = cty;
});
if (err) {
callBack(err, null);
} else {
callBack(null, result);
}
});
}
function getOrganisations(callBack) {
var result=[];
connection.query('SELECT * FROM organisations ', (err, rows) => {
if(err) throw err;
rows.forEach(function(org) {
result[org.id] = org;
});
if (err) {
callBack(err, null);
} else {
callBack(null, result);
}
});
};
module.exports = router;
I always get the error
TypeError: Cannot set properties of undefined (setting 'city')
at /opt/alarmprocessor/routes/settings.js:53:32
. . .
which is the line result[r].city = cty[item.city_id].name;
King regards for helping me out :)
Tried to set it as an array, as an Object, made console outputs everywhere... seems all to be fine.. Maybe i am too new to NodeJS so it hasnt been clicked in my Head ;D
This error occur because result[r] doesn't exist line 53. It's declared but doesn't "exist" -> it's undefined. If you perform a mere console.log(result[r]); line 52 you will get an undefined, and you can't set properties (like city) to an undefined value.
The quick fix would be to use optional chaining like this:
result[r]?.city = cty[item.city_id].name;
it won't fix your code, it will only stop crashing by ignoring the assignment.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
Good afternoon, I'm now learning nodeJS. Right now I have a problem with a function I devised and I can't seem to find the problem
var http = require('http');
var mysql = require('mysql');
var url = require('url');
http.createServer(function (req, res) {
if (req.url === '/favicon.ico') {
//res.writeHead(200, {'Content-Type': 'image/x-icon'} );
res.end();
//console.log('favicon requested');
return;
}
var con = mysql.createConnection({
host: "localhost",
user: "admin",
password: "admin",
database: "brandibDB"
});
res.writeHead(200, {'Content-Type': 'text/plain'});
var addr = url.parse(req.url, true);
var teste = addr.pathname.split('/');
var tester = getJson(teste,con);
console.log(tester);
//console.log(teste);
//res.write(teste[1] + teste[2]);
res.end();
}).listen(8080);
function getJson(teste,con){
var resultado = "";
con.connect(function(err){
if (err) throw err;
if(teste[1] == 'users'){
if(teste.length!=2){
var str = "SELECT * FROM tblUsers where id ="+ mysql.escape(teste[2]) + "";
con.query(str, function (err, result, fields) {
if (err) throw err;
resultado = JSON.stringify(result);
});
}
else{
var str = "SELECT * FROM tblUsers";
con.query(str, function (err, result, fields) {
if (err) throw err;
resultado = JSON.stringify(result);
});
}
}
});
con.end;
return resultado;
}
console.log('Isto deve ser uma consola');
right now I have data in a db and I'm testing accessing this server with url: "localhost:8080/users/1" or "localhost:8080/users/"
When I run the second link for example, it should go into the function getJson and return the right value(a json with all the user registries) but it just returns empty. I've tried putting console logs inside the function around the lines "resultado = Json..." and it displays the right value. The result is lost afterwards.
Any tips?
It's because your getJson function has returned before the asynchronous callback from the query has completed, therefore resultado is still an empty string.
Instead you could either provide a callback to getJson that gets called when the query returns, or better still, use promises. Something like this:
var tester = getJson(teste, con)
.then(result => {
console.log('the results were:', results)
})
.catch(err => {
console.log('Something went wrong', err);
});
and your function becomes something like:
function getJson(teste, con) {
return new Promise((resolve, reject) => {
con.connect(function (err) {
if (err) throw err;
if (teste[1] == 'users') {
if (teste.length != 2) {
var str = "SELECT * FROM tblUsers where id =" + mysql.escape(teste[2]) + "";
con.query(str, function (err, result, fields) {
if (err) return reject(err);
return resolve(JSON.stringify(result));
});
} else {
var str = "SELECT * FROM tblUsers";
con.query(str, function (err, result, fields) {
if (err) return reject(err);
return resolve(JSON.stringify(result));
});
}
}
});
})
}
I'd also recommend ditching var and using let and const, it's much easier to keep track of your variable scopes that way.
I have used wikipedia-js for this project. This is my code for summary.js file.
var wikipedia = require("wikipedia-js");
var something = "initial";
module.exports = {
wikitext: function(topicname) {
console.log("Inside wikitex funciton :" + topicname);
var options = {
query: topicname,
format: "html",
summaryOnly: false,
lang: "en"
};
wikipedia.searchArticle(options, function(err, htmlWikiText) {
console.log("Inside seararticlefunciton :");
if (err) {
console.log("An error occurred[query=%s, error=%s]", topicname, err);
return;
}
console.log("Query successful[query=%s, html-formatted-wiki-text=%s]", topicname, htmlWikiText);
something = htmlWikiText;
});
return something;
},
};
This module I am using in /wiki/:topicname route. The corresponding code in index.js is like this.
router.get('/wiki/:topicname', function(req, res, next) {
var topicname = req.params.topicname;
console.log(topicname);
var first = summary.wikitext(topicname);
res.send("Hello "+first);
});
The problem is, everytime i visit a wiki/some-topic, the last return statement of summary.js executes before htmlWikiText is populated with content. So I always see hello initial on the browser page. Although after sometime it gets printed on terminal due to console.log statement.
So how should I resolve this issue?
I'm not going to try turning this code into synchronous. I'll just correct it to work as an asynchronous version.
You need to pass in callback to wikitext() and return the value in that callback. Here is the revised code of wikitext() and the route that calls it:
var wikipedia = require("wikipedia-js");
module.exports = {
wikitext: function(topicname, callback) {
console.log("Inside wikitex funciton :" + topicname);
var options = {
query: topicname,
format: "html",
summaryOnly: false,
lang: "en"
};
wikipedia.searchArticle(options, function(err, htmlWikiText) {
console.log("Inside seararticlefunciton :");
if (err) {
console.log("An error occurred[query=%s, error=%s]", topicname, err);
return callback(err);
}
console.log("Query successful[query=%s, html-formatted-wiki-text=%s]", topicname, htmlWikiText);
callback(null, htmlWikiText);
});
}
};
router.get('/wiki/:topicname', function(req, res, next) {
var topicname = req.params.topicname;
console.log(topicname);
summary.wikitext(topicname, function(err, result) {
if (err) {
return res.send(err);
}
if (!result) {
return res.send('No article found');
}
res.send("Hello "+result);
});
});
I have the following code running on a node server # heroku. The trouble I am having is that the application frequently fails to create a new parse.com object on post. What is strange is that this code works 100% of the time on my local machine. running through heroku introduces the issue.
I run a heroku log trail when the application posts and it does not throw any exceptions/errors, so I'm stumped as to what to look for.
BTW - I realize this code isn't the prettiest, this is my first attempt to get a node/heroku/parse application up and running.
var http = require('http');
var url = require('url');
var path = require('path');
var fs = require('fs');
var Parse = require('parse/node').Parse;
var mime = require('mime');
var server = http.createServer(router).listen(process.env.PORT || 5000);
Parse.initialize("key", "key");
console.log("Parse initialized");
function router (req, res) {
var pathname = url.parse(req.url, true).pathname;
if (pathname.slice(0, 4) === '/api') {
apiHandler(req, res);
} else {
if (pathname[pathname.length - 1] === '/')
pathname += 'index.html';
staticFileHandler(pathname, res);
}
}
function staticFileHandler (pathname, res) {
fs.readFile(__dirname + '/public_html' + pathname, function (err, data) {
if (err) return errHandler(err, res);
console.log('[200]: ' + pathname);
res.setHeader('Content-Type', mime.lookup(path.extname(pathname)));
res.end(data);
});
}
function errHandler (err, res) {
if (err.code === 'ENOENT') {
res.statusCode = 404;
res.end('File not found!');
console.log('[404]: File not found: ' + err.path);
} else {
console.error(err);
}
}
function apiHandler (req, res) {
if (req.method === 'GET') {
//send back a list of todos
// var toDo = new Parse.Object("ToDo");
var parseQuery = new Parse.Query("ToDo");
parseQuery.find({
success: function(toDoList){
res.setHeader('Content-Type', mime.lookup('json'));
res.end(JSON.stringify(toDoList));
},
error: function(toDoList, error) {
// error is an instance of Parse.Error.
console.log('Error encountered while getting Parse objects: ' + error.message);
}
});
} else if (req.method === "POST"){
var body = "";
req.on('data', function (chunk) {
body += chunk;
});
var today = new Date();
req.on('end', function () {
var toDo = new Parse.Object("ToDo");
toDo.set('Description', body);
toDo.set('Done', false);
toDo.set('DueDate',today )
toDo.save(null, {
success: function(toDo) {
// Execute any logic that should take place after the object is saved.
console.log('New object created with objectId: ' + toDo.id);
},
error: function(toDo, error) {
// Execute any logic that should take place if the save fails.
// error is a Parse.Error with an error code and message.
console.log('Failed to create new object, with error code: ' + error.message);
}
});
});
res.end();
}
}
I have written a nice little error reporting middleware, that sits after all the GET and POST handling (after app.use(app.router); ). See below.
This works great for simple quick GET and POST that goes to the PostGIS database etc.
But I have one POST request that is designed to create a bunch of directories, a number of files, and then spawn 1 -> 8 child_processes tasks
childProcess.execFile(job.config.FMEPath, ["PARAMETER_FILE", job.fmeParamFile], { cwd: job.root },
All that setup does not take much time (less than a second, and it is all async (I use the async library at one point to sequence 5 steps (see below).
My issue is error handling. Right now I return a response immediately before creating all the files and doing all the steps. This means that next(err) is not working as expected. What is a good paradigm for reporting back the errors? I am using WINSTON to log errors [logger.log() ], but should I just log the errors on the server, or should I also report it to the original request. here is the current post request (and remember, I would have to keep the rest, and req and next object around for quite a while to be able to call next(err).
exports.build = function (req, res, next) {
var config = global.app.settings.config;
var jobBatch = groupJobs(req.body.FrameList);
var ticket = tools.newGuid("", true);
var fileCount = req.body.FrameList.length * nitfMultiplier;
var ts = timespan.fromSeconds(fileCount / config.TileRate);
var estimate = ts.hours + ":" + tools.pad(ts.minutes, 2) + ":" + tools.pad(ts.seconds, 2);
res.set({ 'Content-Type': 'application/json; charset=utf-8' });
res.send({ ticket: ticket, maxTiles: fileCount, timeEstimate: estimate, tileRate: config.TileRate, wwwURL: config.WWWUrl });
jobBatchRoot(req, res, jobBatch, config, ticket, next);
};
jobBatchRoot() (I will then go off and do a lot of processing, I did not include all that code.
exports.bugs = function (err, req, res, next) {
global.app.settings.stats.errors += 1;
if (err.code == undefined) {
err.code = 500;
err.message = "Server Error";
}
res.status(err.code);
logger.log('error', '%s url: %s status: %d \n', req.method, req.url, err.code, { query: req.query, body: req.body, message: err.message, stack: err.stack });
var desc = req.method + " " + req.url;
var body = util.format("%j", req.body);
var query = util.format("%j", req.query);
var stack = err.stack.split('\n');
res.format({
text: function () {
res.send(util.format("%j", { title: err.message, code: err.code, desc: desc, query: query, message: err.message, stack: err.stack, body: body}));
},
html: function () {
query = tools.pretty(req.query);
res.render('error', { title: err.message, code: err.code, desc: desc, query: query, message: err.message, stack: stack, body: body });
},
json: function () {
res.send({ title: err.message, code: err.code, desc: desc, query: query, message: err.message, stack: err.stack, body: body });
}
});
};
Perhaps I should be re-factoring this (maybe object oriented), anyway I thought I would post the full module here and all I am looking for is a few tips on structure, best practices.
var util = require('util');
var query = require("pg-query");
var timespan = require('timespan');
var _ = require('lodash');
var path = require('path');
var fs = require('fs');
var query = require("pg-query");
var async = require("async");
var childProcess = require("child_process");
var tools = require("../tools/tools");
var nitfMultiplier = 99;
var manifestVersionID = 5;
exports.setup = function (app) {
};
exports.estimate = function (req, res, next) {
var config = global.app.settings.config;
var fileCount = req.body.FrameList.length * nitfMultiplier;
var ts = timespan.fromSeconds(fileCount / config.TileRate);
var estimate = ts.hours + ":" + tools.pad(ts.minutes, 2) + ":" + tools.pad(ts.seconds, 2);
res.set({ 'Content-Type': 'application/json; charset=utf-8' });
res.send({ ticket: "Estimate", maxTiles: fileCount, timeEstimate: estimate, tileRate: config.TileRate, wwwURL: config.WWWUrl });
};
exports.build = function (req, res, next) {
var config = global.app.settings.config;
var jobBatch = groupJobs(req.body.FrameList);
var ticket = tools.newGuid("", true);
var fileCount = req.body.FrameList.length * nitfMultiplier;
var ts = timespan.fromSeconds(fileCount / config.TileRate);
var estimate = ts.hours + ":" + tools.pad(ts.minutes, 2) + ":" + tools.pad(ts.seconds, 2);
res.set({ 'Content-Type': 'application/json; charset=utf-8' });
res.send({ ticket: ticket, maxTiles: fileCount, timeEstimate: estimate, tileRate: config.TileRate, wwwURL: config.WWWUrl });
jobBatchRoot(req, res, jobBatch, config, ticket, next);
};
function groupJobs(list) {
var jobBatch = {};
_.forEach(list, function (obj) {
if (jobBatch[obj.type] == undefined) {
jobBatch[obj.type] = [];
}
jobBatch[obj.type].push(obj);
});
return jobBatch;
};
function jobBatchRoot(req, res, jobBatch, config, ticket, next) {
var batchRoot = path.join(config.JobsPath, ticket);
fs.mkdir(batchRoot, function (err) {
if (err) return next(err);
var mapInfoFile = path.join(batchRoot, "MapInfo.json");
var mapInfo = {
Date: (new Date()).toISOString(),
Version: manifestVersionID,
Zoom: req.body.Zoom,
CenterLat: req.body.CenterLat,
CenterLon: req.body.CenterLon
};
fs.writeFile(mapInfoFile, tools.pretty(mapInfo), function (err) {
if (err) return next(err);
spawnJobs(req, res, batchRoot, mapInfo, config, ticket, jobBatch, next);
});
});
};
function spawnJobs(req, res, root, mapInfo, config, ticket, jobBatch, next) {
_.forEach(jobBatch, function (files, key) {
var job = {
req: req,
res: res,
type: key,
files: files,
batchRoot: root,
mapInfo: mapInfo,
config: config,
ticket: ticket,
missingFiles: [],
run: true,
next: next
};
setup(job);
});
};
function setup(job) {
job.root = path.join(job.batchRoot, job.type);
job.fmeParamFile = path.join(job.root, "fmeParameters.txt");
job.fmeWorkSpace = path.join(job.config.LibrarianPath, "TileBuilder.fmw");
job.fmeLogFile = path.join(job.root, "jobLog.log");
job.errorLog = path.join(job.root, "errorLog.log");
job.jobFile = path.join(job.root, "jobFile.json");
job.manifestFile = path.join(job.root, "manifest.json");
async.series({
one: function (callback) {
maxZoom(job, callback);
},
two: function (callback) {
fs.mkdir(job.root, function (err) {
if (err) return job.next(err);
callback(null, "Job Root Created");
});
},
three: function (callback) {
makeParamFile(job, callback);
},
four: function (callback) {
delete job.req;
delete job.res;
fs.writeFile(job.jobFile, tools.pretty(job), function (err) {
if (err) return job.next(err);
callback(null, "Wrote Job File");
});
},
five: function (callback) {
runJob(job, callback);
},
six: function (callback) {
tileList(job, callback);
},
seven: function (callback) {
finish(job, callback);
},
},
function (err, results) {
if (err) return job.next(err);
console.log(tools.pretty(results));
});
}
function maxZoom(job, callback) {
var qString = util.format('SELECT type, "maxZoom" FROM portal.m_type WHERE type=\'%s\'', job.type);
query(qString, function (err, rows, result) {
if (err) {
var err = new Error(queryName);
err.message = err.message + " - " + qString;
err.code = 400;
return job.next(err);
}
job.maxZoom = rows[0].maxZoom - 1; // kludge for 2x1 root layer in leaflet
return callback(null, "Got MaxZoom");
});
}
function makeParamFile(job, callback) {
var text = util.format("%s\n", job.fmeWorkSpace);
text += util.format("--OutputDir %s\n", job.root);
text += util.format("--LogFile %s\n", job.fmeLogFile);
var source = "";
_.forEach(job.files, function (file) {
var path = ('development' == process.env.NODE_ENV) ? file.path.replace(job.config.SourceRootRaw, job.config.SourceRoot) : file.path;
if (fs.existsSync(path)) {
source += wrap(path, '\\"') + " ";
}
else {
job.missingFiles.push(path);
}
});
source = wrap(wrap(source, '\\"'), '"');
text += "--Sources " + source;
if (job.missingFiles.length == job.files.length) job.run = false;
fs.writeFile(job.fmeParamFile, text, function (err) {
if (err) return job.next(err);
return callback(null, "Wrote Paramaters File");
})
};
function wrap(content, edge) {
return edge+content+edge;
}
function runJob(job, callback) {
if (!job.run) return callback(null, "Skipped JOB, no files");
childProcess.execFile(job.config.FMEPath, ["PARAMETER_FILE", job.fmeParamFile], { cwd: job.root },
function (err, stdout, stderr) {
if (err) return job.next(err);
job.stdout = stdout;
job.stderr = stderr;
var bar = "\n--------------------------------------------------------------------------------------------------------\n";
var results = util.format("%s STDOUT: \n %s%s STDERR: \n %s", bar, job.stdout, bar, job.stderr);
fs.appendFile(job.fmeLogFile, results, function (err) {
return callback(err, "FME JOB " + job.type + " run completed");
});
});
}
function tileList(job, callback) {
var tiles = [];
var byteCount = 0;
fs.readdir(job.root, function (err, files) {
if (err) {
logger.log('error', 'tileList directory read: %s \n', job.root, { message: err.message, stack: err.stack });
return job.next(err);
}
async.each(files, function (file, done) {
var fileName = file.split(".");
fs.lstat(job.root + "\\" + file, function (err, stats) {
if (!err && stats.isFile() && (fileName[1] == "png")) {
tiles.push({ id: fileName[0], size: stats.size });
byteCount += stats.size;
};
done(null);
});
}, function (err) {
job.tileList = tiles;
job.byteCount = byteCount;
return callback(null, "got tile list");}
);
});
}
function finish(job, callback) {
var manifest = {
Date: (new Date()).toISOString(),
Version: manifestVersionID,
MaxZoom: job.maxZoom,
Class: "OVERLAY",
FileCount: job.tileList.length,
Size: job.byteCount / (1024 * 1024), // Mbytes
files: job.tileList
};
fs.writeFile(job.manifestFile, tools.pretty(manifest), function (err) {
if (err) {
logger.log('error', 'manifest write: %s \n', job.manifestFile, { message: err.message, stack: err.stack });
return job.next(err);
}
return callback(null, "JOB " + job.type + " completed");
});
}
I went and re-factored this. I created a module, with module.exports = function(..) {...}
and then added lots of state and methods to create a class. That contains the Job definition. So now I create the top level directories, return a response, and spawn the sub jobs. They all run async after the express response. But they should not get errors, and if they do, then I use WINSTON to log them in the server, and also return a job done information to the user when all the builds are done.