I know that it is possible to do this, but my question is whether there is a good reason not to do it, or is there a better way? I feel like it could make the code easier to follow and potentially make testing easier, but that's as far as I've gotten.
Example Code:
postSchema.methods.publishPost = function(data, cb) {
var self = this,
options = options || {};
mongoose.model('Version').findById(data.version).exec(function(err, version) {
if (err) return cb(err);
mongoose.model('Translation').findTranslatedVersion(version, function(err, translation) {
if(err) return cb(err);
publisher.publish(self, translation ? translation : version, function(err, published) {
if (err) return cb(err);
self.save(published);
});
});
});
}
Related
I'm having an issue with what I believe is the scope of a variable in mongoose. My code is this:
var blogUserId;
blogs.forEach(function(blog, index) {
User.findOne({'username': blog.username}, function(err, user) {
blogUserId = user._id;
console.log(blogUserId);
});
console.log(blogUserId);
Blog.find({'title': blog.title}, function(err, blogs) {
if (!err && !blogs.length) {
console.log(blogUserId);
Blog.create({title: blog.title, author: blogUserId, body: blog.body, hidden: blog.hidden});
}
if (err) {
console.log(err);
}
});
});
This is part of a seed file just for development, but I'm rather confused why it wouldn't be working. blogs is just an array of objects to load into the collection. I've searched on all similar answers but I haven't found a correct answer that would explain this.
Your blogUserId is not set when your Blog.find() is called. You'd have to nest it differently, something like this:
var blogUserId;
blogs.forEach(function(blog, index) {
User.findOne({'username': blog.username}, function(err, user) {
blogUserId = user._id;
console.log(blogUserId);
Blog.find({'title': blog.title}, function(err, blogs) {
if (!err && !blogs.length) {
console.log(blogUserId);
Blog.create({title: blog.title, author: blogUserId, body: blog.body, hidden: blog.hidden});
}
if (err) {
console.log(err);
}
});
});
});
I haven't tested it so I'm not sure that there are no other errors in your code but was definitely a problem that you were calling Blog.find that expected the blogUserId to be set possibly before it was set in the User.findOne callback.
It can be written in a more readable way with named callbacks.
When working in Node you need to keep in mind that you are working in an asynchronous environment.
User.find().exec(function (err, users) {
if (err){
callback(err);
} else {
callback(users);
}
});
User.find(function (err, users) {
if (err) {
callback (err);
} else {
callback(users);
}
});
What is the benefit of using the top code? both seem to work equally well
They are identical and there's no benefit in your example
When you do not pass a callback to find function it won't execute but instead returns a query then you need to use exec()
var query = User.find();
now you can add some more criteria
query.where({age: 15});
and some more
query.select({name:1}); // or {firstname:1, lastname:1} etc.
now you've built up your query so to get the results you need to execute it.
query.exec(function(err, users){
});
But you can also do this like
User.find({age:15}, {name:1}, function(err, users){
});
Above is identical to
User.find({age:15}, {name:1}).exec(function(err, users){
});
since there's no callback in find function it will return query which means no results, exec will give you the results
Here's a code snippet
var zip = new require('node-zip')(data, {base64: false, checkCRC32: true});
for (var i in zip.files) {
fs.writeFile(path.join(to, i), zip.files[i], function(err) {
if (err) { error = err; }
});
}
Is there any way to be aware of when they're all executed?
A very commonly used module for handling many types of asynchronous operations is the async.js module. In fact, it's the most depended upon module in NPM behind underscore.
In your case I recommend the async.series function.
var async = require('async');
var zip = new require('node-zip')(data, {base64: false, checkCRC32: true});
async.eachSeries(zip.files, function(file, cb) {
fs.writeFile(path.join(to, i, file, function(err) {
if(err) return cb(err);
cb();
});
},
function(err) {
if(err) throw err;
}
);
Common problem. Try each-async which abstracts the counter method so you don't have to write it yourself. You can also look at promises, but that might not work if your code doesn't use promises already; it can be weird to mix promises and callback code.
I'm trying to export one function this way:
exports.query = function(request){
conn.query(request, function(err, rows, fields){
if(err) console.log(err);
return rows[0].id;
});
}
and using it:
var mysql = require('./mysql');
console.log(mysql.query('SELECT * FROM tablename'));
Proceeding this way for getting a result involves undefined as output.
How do I to fix this, please?
Note that when I just type console.log(rows[0].id) instead of return rows[0].id it sends back 123.
Thanks in advance!
In your example, the output is being returned to the anonymous function of the database query instead of the caller of the module. You can use a callback to return output to the caller of the module.
exports.query = function(request, callback){
conn.query(request, function(err, rows, fields){
if (err) {
callback(err);
} else {
callback(null, rows[0].id);
}
});
}
Then call it like
var mysql = require('./mysql');
mysql.query('SELECT * FROM tablename', function(err, results){
if (err) {
console.error(err);
} else {
console.log(results);
}
});
That's a problem of synchrony.
the conn.query function returns undefined because it finish its execution before the results are fetched (like almost any i/o related operation on js/node).
One possible solution to that, is to provide a callback to your query function.
exports.query = function(request, cb){
conn.query(request, function(err, rows, fields){
// some "special" processing
cb(err, rows, fields);
});
};
If you're not familiar with async functions, take a look on some articles about that:
http://justinklemm.com/node-js-async-tutorial/
https://www.promisejs.org/
thanks for your help...struggling big time with how to handle this properly. I'm in async now, having given up on my ability to write the callbacks properly. I have snippet where I'm passing a set of random numbers (eachrecord) and passing them through to a mongoose call. Trying to create a data set from the multiple queries I pass.
My issue is that no matter what I've done for 4 hours, the "newarray" variable is always empty.
Thank you for your help -
async.forEach(arLimit, function(eachrecord, callback){
newarray = new Array;
var query = UGC_DB_Model.find({}).skip(eachrecord).limit(-1);
query.execFind(function (err, data) {
if (err)
console.log(err);
else {
newarray.push(data);
}
});
callback(null, newarray);
}, function(err, result) {
if (err) return next(err);
console.log("(it's empty): " + result);
});
There are several issues with your code:
async.forEach isn't meant to 'generate' results, that's what async.map is for;
you need to call the callback only when execFind is done, and not immediately after calling it;
your newarray is probably not necessary;
So try this instead:
async.map(arLimit, function(eachrecord, callback){
var query = UGC_DB_Model.find({}).skip(eachrecord).limit(-1);
query.execFind(function (err, data) {
if (err)
callback(err); // pass error along
else {
callback(null, [ data ]);
// although I think you mean this (because 'data' is probably an array already)
// callback(null, data);
}
});
}, function(err, result) {
if (err) return next(err);
console.log("(it's empty): " + result);
});