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);
Related
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 '}'
In the below code, users.push used within ‘db.each’ wont work. However, if I move ‘users.push’ outside then it seems to work.
How can I push the new objects from db.each into the users array?
let db = new sqlite3.Database('./db/main.db', (err) => {
if (err) console.error(err.message);
console.log('Connected to the main database.');
});
var users = [];
db.serialize(() => {
db.each(`SELECT email, name FROM users`, (err, row) => {
if (err) console.error(err.message);
let user = {
email: row.email,
name: row.name
}
users.push(user);
});
});
console.log(JSON.stringify(users));
db.close();
I am using express and sqlite3 node packages.
It's because db.serializeand db.each are asynchronous functions (and return immediately, thus executing console.log before the db callbacks are executed).
Here should be a working example :
db.serialize(() => {
db.each(`SELECT email,
name
FROM users`, (err, row) => {
if (err) {
console.error(err.message);
}
let user = {
email : row.email,
name : row.name
}
users.push(user);
console.log(JSON.stringify(users));
db.close();
});
});
First error: asynchronicity not handled properly
As Antoine Chalifour pointed out, you call console.log(JSON.stringify(users)); before users gets modified in the asynchronous callback. Refer to his answer for fix and explanations.
Second error: errors not handled
You wrote if (err) { console.error(err.message); } then go on with the rest of the function. That is bad, because an error might happen and you'd just continue with your program. You should instead write something like:
if (err) {
console.error(err);
return;
}
or:
if (err) throw err;
I've seen a similar question once, but I can't for the life of me figure out why this isn't working. I have a pretty simple program below that should wrap the exec function and return the result. However all it returns is undefined. Here's the function:
var exec = require('child_process').execSync;
quickexec = function(command) {
exec(command, function(error, stdout, stderr) {
if(error) {
return error;
} else {
return stdout;
}
});
};
I call it like this console.log(quickexec('echo -n $USER')); and I get undefined everytime. However if I change the return in my function to a console.log it works. I thought that it was an async problem which is why I started using execSync, but it didn't change anything.
quickexec() does not actually return anything. The return inside it are in the async callback which happens long after quickexec() has already returned. You can't synchronously return an asynchronous result. This is a common issue when learning how to do proper asynchronous programming in node.js.
If you need a synchronous result, you can use execsync(), but usually the best design is to use the asynchronous result in the callback.
var quickexec = function(command, callback) {
exec(command, function(error, stdout, stderr) {
if(error) {
callback(error);
} else {
callback(null, stdout);
}
});
};
quickexec('echo -n $USER', function(err, result) {
// use the result here in the callback
if (err) {
console.log(err);
} else {
console.log(result);
}
});
I keep running into this pattern when coding in Meteor where I find myself making multiple method calls nested within each other - first method fires, then in the callback, a second one fires which is dependent on the first one's result, etc. Is there a better pattern for using multiple methods without nested method calls inside callbacks? The code quickly gets messy.
Meteor.call('unsetProduct', product._id, omitObj, function(err, result) {
if(!err) {
Meteor.call('editProduct', product._id, object, function(err, result) {
if(!err) {
//if no error, then continue to update the product template
Meteor.call('editProductTemplate', self._id, obj, function(err, result) {
if(!err) {
//call some other method
}
else {
FormMessages.throw(err.reason, 'danger');
}
});
}
else {
FormMessages.throw(err.reason, 'danger');
}
});//end edit product
}
else {
AppMessages.throw(err.reason, 'danger');
}
});`
Take a look at reactive-method package. I think it does exactly what you need: it wraps asynchronous Meteor.calls into synchronous code. With it, your code would look cleaner, like
try {
const result = ReactiveMethod.call('unsetProduct', product._id, omitObj);
} catch (error) {
AppMessages.throw(err.reason, 'danger');
}
try {
const nestedResult = ReactiveMethod.call('editProduct', product._id, object);
} catch (error) {
FormMessages.throw(err.reason, 'danger');
}
try {
const evenMoreNestedResult = ReactiveMethod.call('editProductTemplate', self._id, obj);
} catch (error) {
FormMessages.throw(err.reason, 'danger');
}
Which will look nicer when you add some logic inside try statements.
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();
});
});