I'm having some trouble getting two try-catches to work within one function, I'm quite new to JS and not exactly familiar with the functionality and limitations for try-catch. Currently working with a mongoDB database (Using mongoose). This is my code:
async function calcPoints() {
ClanMember.countDocuments({}, async function (err, count) {
for(var i = 0; i < count; i++) {
ClanMember.findOne({
_id: i
}, async (err, res) => {
try {
//removed most of the code as it's quite long and irrelevant to my issue, just collecting info here
PointTrack.findOne({
membershipId: membershipId
}, async (err, res) => {
try {
//removed the code, if the id was found it updates the document
}
catch(err) {
//removed the code, if the id was not found it creates a new document
done(err, null);
}
})
}
catch(err) {
console.log("Something went wrong");
done(err, null);
}
})
}
})
};
When i try to run the code i get this error code:
const { catch } = require("../database/database");
^
SyntaxError: Unexpected token '}'
Related
Ok, so I need to connect to a MySQL database through SSH and the connection works fine. I am able to execute queries with no problem. I can also get the results and print it out. The thing is, I need something simpler since I will have to send a lot of queries to this database. Below is the code for a promise which creates a connection to this database.
const SSHConnection = new Promise((resolve, reject) => {
sshClient.on('ready', () => {
sshClient.forwardOut(
forwardConfig.srcHost,
forwardConfig.srcPort,
forwardConfig.dstHost,
forwardConfig.dstPort,
(err, stream) => {
if (err) reject(err);
const updatedDbServer = {
...dbServer,
stream
};
const connection = mysql.createConnection(updatedDbServer);
connection.connect((error) => {
if (error) {
reject(error); // Return error
}
resolve(connection); // OK : return connection to database
});
});
}).connect(tunnelConfig);
});
Then I have this piece of code that gives me access to said connection and allows me to send queries. The problem is that I need the return value of my queries and be able to use it in other modules for my project. For example, export a single function to be used to send queries like sendQuery('Enter SQL here').
function sendQuery(sql) {
SSHConnection.then(
function(connection) {
connection.query(
sql,
function(err, results, fields) {
return results; // <---------- I want to return this from the function 'sendQuery()'
}
);
},
function(error) {
console.log("Something wrong happened");
}
);
}
I can work with the results inside SSHConnection.then() but this isn't functional for me.
I would like to have something similar below to work.
// Main function
(async function() {
let res = sendQuery(`SELECT 23+2 AS Sum;`);
console.log(res); // Outputs Sum: 25
})();
So to my question. Is there a way to access the results from a query inside of a promise.then(), from the outside?
I think the problem is you need to add another return statement to your code.
function sendQuery(sql) {
return SSHConnection.then(
function(connection) {
return connection.query(
sql,
function(err, results, fields) {
return results; // <---------- I want to return this from the function 'sendQuery()'
}
);
},
function(error) {
console.log("Something wrong happened");
}
);
}
This should return the results from the query properly IF connection.query returns a promise. I'm not sure if it does. If it does then you can just execute the function like so.
// Main function
(async function() {
let res = await sendQuery(`SELECT 23+2 AS Sum;`);
console.log(res); // Outputs Sum: 25
})();
If connection.query does not return a promise then I suppose you could wrap it in a promise like so.
function sendQuery (sql) {
return SSHConnection.then(
function (connection) {
return new Promise((resolve, reject) => {
connection.query(
sql,
function (err, results, fields) {
if (err)reject(err)
resolve(results) // <---------- I want to return this from the function 'sendQuery()'
}
)
})
},
function (error) {
console.log('Something wrong happened')
}
)
}
love the name by the way...really liked that movie. As to your question, I'd suggest a couple of things:
if you have a lot of queries coming, you might consider moving the connection independent of the query, so that the connection setup and teardown isn't part of the cost of time for the query itself. If you have a single connection, single DB, etc., then you could instantiate the connection once, at startup, and then leave the connection open, and reference it in your queries.
your question:
function sendQuery(sql) {
const resValues = await SSHConnection.then(
function(connection) {
connection.query(
sql,
function(err, results, fields) {
return results; // <---------- I want to return this from the function 'sendQuery()'
}
);
},
function(error) {
console.log("Something wrong happened");
}
);
return(resValues); // or handle error cases
}
Note I added a return value from the ".then()" call, which captures your "results" return value, and then returns that from the parent function (sendQuery)
I have created Node JS app, i am explicitly downloading more than 100000 records from database. While the request is in proceeding, i try to login in the same application from another browser it wont respond unless the previous request is completed. Any idea ? Anything to do with event loop or threads ??
So here is my logic.
I make a get request to my API in Step 1, API calls database layer in Step 2. Database is returning only 21 records, i am explicitly looping 100000*21 to make heavy rendering just to test the load on json2csv. While doing so, the other requests to server will not respond until the last processing completes.
Step 1:
router.get('/report/downloadOverdueTrainings/:criteria', function (req, res, next) {
var overDueTrainings = [];
var reportManager = new ReportManager();
var result = reportManager.getOverdueTrainings(JSON.parse(req.params.criteria));
result.then(function (result) {
var fields = ['Over Due Trainings'];
for (var i = 0; i < 100000; i++) { //Testing purpose
for (var training of result) {
overDueTrainings.push({
'Over Due Trainings': training.OverDueTrainings
})
}
}
json2csv({
data: overDueTrainings,
fields: fields
}, function (err, csv) {
if (err)
throw err;
res.setHeader('Content-disposition', 'attachment; filename=OverdueTrainings.csv');
res.set('Content-Type', 'text/csv');
res.send(csv);
});
}).catch(function (err) {
next(err);
});
});
Step 2: Database Logic
var xtrDatabaseConnection =require('./xtrDatabaseConnection').XTRDatabaseConnection;
ReportData.prototype.getOverdueTrainings = async function (params) {
var connection = new xtrDatabaseConnection();
var sequelize = connection.getSequelize();
try {
var query = "CALL report_getOverdueTrainings(:p_CourseCode,:p_Revision,:p_RevisionNo,:p_UserGroup,:p_Username,:p_Status,:p_SortColoumnNo,:p_SortColoumnDirection,:p_callType,:p_StartIndex,:p_PageSize)";
var replacements = {
p_CourseCode: params.CourseCode,
p_Revision: params.Revision,
p_RevisionNo: (params.RevisionNo == '' || params.RevisionNo == null) ? 0 : params.RevisionNo,
p_UserGroup: params.UserGroup,
p_Username: params.Username,
p_Status: params.Status,
p_SortColoumnNo: params.SortColoumnNo,
p_SortColoumnDirection: params.SortColoumnDirection,
p_callType: params.callType,
p_StartIndex: params.startIndex,
p_PageSize: params.pageSize
};
//console.log(replacements);
return await connection.executeQuerySequelize(sequelize, query, Sequelize.QueryTypes.RAW, replacements);
} catch (e) {
throw (e);
} finally {
//To close connections
sequelize.connectionManager.close().then(() => console.log('Connection Closed'));
}
}
XTRDatabaseConnection.prototype.executeQuerySequelize = function (sequelize, query, queryType, replacements) {
return sequelize.authenticate()
.then(() => {
return sequelize.query(query, {
replacements: replacements,
type: queryType
}).
then(result => result)
.catch(err => {
throw (err);
});
})
.catch(err => {
xtrUtility.logErrorsToWinstonTransports('Unable to connect to the database or some error occurred while executing your query. ', err);
throw new AppError(err, "dbError", "Unable to connect to the database or some error occurred while executing your query.");
});
}
NodeJS is single-thread, single process. As long as a javascript function is running, nothing else can run. This is by design. The event loop will only kick in again once your functions have stopped executing.
This is blocking:
for (var i = 0; i < 100000; i++) { //Testing purpose
for (var training of result) {
overDueTrainings.push({
'Over Due Trainings': training.OverDueTrainings
})
}
}
until the execution is finished.
also
var result = reportManager.getOverdueTrainings(JSON.parse(req.params.criteria));
result.then(function (result) {
/****/
return await connection.executeQuerySequelize(sequelize, query, Sequelize.QueryTypes.RAW, replacements);
Node.js is working on single thread. So you need use it for small process with async calls. When you use async/await you are blocking the callback function.
So you need to use your database functions as promise. It will work async.
.query('CALL someFunction()')
.then(function(response){
//done
}).error(function(err){
//error
});
But there is a problem about it. You need to load 21 times your result. So in this case you need to use recursive function. When you finished recursive call you can send your data to client as async.
I'm trying to learn Node.js via Express.js framework. Currently I need to make an API call to get some data usefull for my app.
The API call is made with Request middleware, but when I'm out of the request my variable become undefined ... Let me show you :
var request = require('request');
var apiKey = "FOOFOO-FOOFOO-FOO-FOO-FOOFOO-FOOFOO";
var characters = [];
var gw2data;
var i = 0;
module.exports.account = function() {
request('https://api.guildwars2.com/v2/characters/?access_token=' + apiKey, function (error, response, body) {
gw2data = JSON.parse(body);
console.log('out request ' + gw2data); // {name1, name2 ...}
for (i; i < gw2data.length; i++) {
getCharacInfo(gw2data[i], i);
}
});
console.log('out request ' + characters); // undefined
return characters;
};
function getCharacInfo (name, position) {
request('https://api.guildwars2.com/v2/characters/' + name + '/?access_token=' + apiKey, function (error, response, body) {
if (!error && response.statusCode == 200) {
characters[position] = JSON.parse(body);
}
});
}
I don't understand why the gw2data variable become undefined when I go out of the request ... Someone can explain me ?
EDIT : I come to you because my problem has changed, I now need to make an API call loop in my first API call, same async problem I guess.
The previous code has evoluate with previous answers :
module.exports.account = function(cb) {
var request = require('request');
var apiKey = "FOOFOO-FOOFOO-FOO-FOO-FOOFOO-FOOFOO";
var characters = [];
var i = 0;
request('https://api.guildwars2.com/v2/characters/?access_token=' + apiKey, function(err, res, body) {
var numCompletedCalls = 1;
for (i; i < JSON.parse(body).length; i++) {
if (numCompletedCalls == JSON.parse(body).length) {
try {
console.log(characters); // {} empty array
return cb(null, characters);
} catch (e) {
return cb(e);
}
}else {
getCharacInfo(JSON.parse(body)[i]);
}
numCompletedCalls++;
}
});
};
function getCharacInfo (name) {
request('https://api.guildwars2.com/v2/characters/' + name + '/?access_token=' + apiKey, function (err, res, body) {
if (!err) {
console.log(characters); // {data ...}
characters.push(JSON.parse(body));
}
});
}
And the call of this function :
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
var charactersInfos = require('../models/account');
charactersInfos.account(function(err, gw2data) {
if (err) {
console.error('request failed:', err.message);
} else {
res.render('index', {
persos : gw2data
});
}
});
});
The problem is, when I return the characters array, it's always an empty array, but when I check in my function getCharacInfo, the array contains the data ...
The reason gw2data is undefined in the second console.log is because your logging it too early. request is an asynchronous operation therefore it will return immediately...however, that doesn't mean the callback will.
So basically what your doing is logging gw2data before it's actually been set. When dealing with asynchronous operations the best approach is to generally make your own method asynchronous as well which can be accomplished in a couple of ways - the simplest being having your function accept a callback which expects the data in an asynchronous way e.g.
module.exports.account = function(cb) {
request(..., function(err, res, body) {
// return an error if `request` fails
if (err) return cb(err);
try {
return cb(null, JSON.parse(body));
} catch (e) {
// return an error if `JSON.parse` fails
return cb(e);
}
});
}
...
var myModule = require('mymodule');
myModule.account(function(err, g2wdata) {
if (err) {
console.error('request failed', err.message);
} else {
console.log('out request', g2wdata);
}
});
Based on your syntax I'm assuming you aren't working with ES6, worth looking at this (particularly if your just starting to learn). With built-in promises & also async-await support coming relatively soon this sort of stuff becomes relatively straightforward (at least at the calling end) e.g.
export default class MyModule {
account() {
// return a promise to the caller
return new Promise((resolve, reject) => {
// invoke request the same way but this time resolve/reject the promise
request(..., function(err, res, body) {
if (err) return reject(err);
try {
return resolve(JSON.parse(body));
} catch (e) {
return reject(e);
}
});
});
}
}
...
import myModule from 'mymodule';
// handle using promises
myModule.account()
.then(gw2data => console.log('out request', gw2data))
.catch(e => console.error('request failed', e));
// handle using ES7 async/await
// NOTE - self-executing function wrapper required for async support if using at top level,
// if using inside another function you can just mark that function as async instead
(async () => {
try {
const gw2data = await myModule.account();
console.log('out request', gw2data);
} catch (e) {
console.log('request failed', e);
}
})();
Finally, should you do decide to go down the Promise route, there are a couple of libraries out there that can polyfill Promise support into existing libraries. One example I can think of is Bluebird, which from experience I know works with request. Combine that with ES6 you get a pretty neat developer experience.
The request API is asynchronous, as is pretty much all I/O in Node.js. Check out the Promise API which is IMO the simplest way to write async code. There are a few Promise adapters for request, or you could use something like superagent with promise support.
I am writing a NodeJS script that will run every hour through Heroku's scheduler. I am quering the Mongo instance I have (mongohq/compose) and then doing something with those results. I am working with Mongoose.js and the find() command. This returns an array of results. With those results I need to perform additional queries as well as some additional async processing (sending email, etc).
Long story short, due to node's async nature I need to wait until all the processing is complete before I call process.exit(). If I do not the script stops early and the entire result set is not processed.
The problem is that I have a christmas tree effect of calls at this point (5 nested asnyc calls).
Normally I'd solve this with the async.js library but I'm having a problem seeing this through with this many callbacks.
How can I make sure this entire process finishes before exiting the script?
Here's the code that I'm working with (note: also using lodash below as _):
Topic.find({ nextNotificationDate: {$lte: moment().utc()}}, function (err, topics) {
if (err) {
console.error(err);
finish();
} else {
_.forEach(topics, function (topic, callback) {
User.findById(topic.user, function (err, user) {
if (err) {
// TODO: impl logging
console.error(err);
} else {
// Create a new moment object (not moment.js, an actual moment mongoose obj)
var m = new Moment({ name: moment().format("MMM Do YY"), topic: topic});
m.save(function(err) {
if(err) {
// TODO: impl logging
console.error(err);
} else {
// Send an email via postmark
sendReminderTo(topic, user, m._id);
// Update the topic with next notification times.
// .. update some topic fields/etc
topic.save(function (err) {
if(err) {
console.error(err);
} else {
console.log("Topic updated.");
}
})
}
})
}
});
console.log("User: " + topic.user);
});
}
});
Part of what is making your code confusing is the usage of else statements. If you return your errors, you won't need the else statement and save 4 lines of indentation for every callback. That in and of itself will make things drastically more readable.
Using async:
Topic.find({nextNotificationDate: {$lte: moment().utc()}}, function (err, topics) {
if (err) {
console.error(err);
return finish(err);
}
async.each(topics, function(topic, topicCallback) {
async.auto({
user: function (callback) {
User.findById(topic.user, callback);
},
moment: function(callback) {
var m = new Moment({name: moment().format("MMM Do YY"), topic: topic});
m.save(callback);
},
topic: ["moment", "user", function (callback, results) {
var m = results.moment;
var user = results.user;
sendReminderTo(topic, user, m._id);
topic.save(callback);
}]
}, function(err) {
if (err) {
return topicCallback(err);
}
console.log("Topic updated.")
return topicCallback();
});
}, function(err) {
if (err) {
console.error(err);
return finish(err);
}
return finish();
});
});
May be this is simple and stupid question, but im just learning my first asynchronous server language and redis is my first key-value db.
Example. I need do this:
$x = users:count
get user:$x
But with asynchronic javascript i get this code
redis-cli.get('users:count', function(err, repl){
if(err){
errorHandler(err);
} else {
redis-cli.get('user:'+repl, function(err, repl){
if(err){
errorHandler(err);
} else {
console.log('We get user '+repl+'!')
}
})
}
})
This code not so large and not nested to much, but it's look like on my firt not example/test project i get crazy nested functions-callbacks.
How solve this and make pretty and readable code?
function getUserCount(callback, err, repl) {
if (err) {
return callback(err);
}
redis-cli.get('user:' + repl, getUser.bind(null, callback));
}
function getUser(callback, err, repl) {
if (err) {
return callback(err);
}
console.log('We get user ' + repl + '!');
}
redis-cli.get('users:count', getUserCount.bind(null, errorHandler));
bind works wonders. If you prefer to have the bind abstracted then you can use this to store state that would normally be stored in closures like:
require("underscore").bindAll({
run: function (errorHandler) {
this.errorHandler = errorHandler;
redis-cli.get('users:count', this.getUserCount);
},
getUserCount: function (err, repl) {
if (err) return this.errorHandler(err);
redis-cli.get('user:' + repl, this.getUser);
},
getUser: function (err, repl) {
if (err) return this.errorHandler(err);
console.log('got user ', repl);
}
}).run(errorHandler);