in the following code I am expecting the function finalCallBack to be executed once we are done iterating through all elements
var rows = [
{ name: 'first'},
{ name: 'second'}
];
var execForEachRow = function(row, callback){
var studentModel = new StudentModel(row);
studentModel.save(function(err,result){
if (err) { throw err;}
rowsSavedCount++;
});
}
var finalCallBack = function(err){
if (err) { msg = err;} else { msg = rowsSavedCount;}
res.send({"result" : msg});
}
async.each(rows, execForEachRow, finalCallBack);
When i execute the above code, it very successfully inserts data into the mongo collection. However the finalCallBack does not get called.
Any clue what I might be missing here ?
You've missed calling callback in studentModel.save's callback:
studentModel.save(function(err,result){
if (err)
return callback(err);
rowsSavedCount++;
callback(null);
});
Also - throwing Exception is not a good idea - it'll break you whole Express server.
Related
Hello try to use an mongodb distinct query with NodeJS (Async). In the GUI of Mongo this query works but in Node it returns the following error
TypeError: db.collection(...).distinct(...).toArray is not a function
The error returns on the following statement:
mongo.connect(uri, function (err, db) {
console.info('MONGODB START CHECK COLLECTIONS')
var tasks = [ // Load businessrules
function (callback) {
db.collection('businessrules').find({typeBusinessRule: 'SpiderGraphExeption'}).toArray(function (err, businessrules) {
if (err) return callback(err);
locals.businessrules = businessrules;
callback();
});
},
// Load stgOmniTracker
function (callback) {
db.collection('stgOmniTracker').find({}).toArray(function (err, tickets) {
if (err) return callback(err);
locals.tickets = tickets;
callback();
});
},
// HERE STARTS THE ERROR
function (callback) {
db.collection('stgOmniTracker').distinct("Responsible Group").toArray(function (err, group) {
if (err) return callback(err);
locals.group = group;
callback();
});
}
];
console.info('--------------- START ASYNC ------------------------')
async.parallel(tasks, function (err) {
if (err) return next(err);
var businessrules = locals.businessrules, tickets = locals.tickets, resultSet = {}, aggCountsPerDayCattegory = [], group = locals.group
db.close()
}
I hope you can help me to fix this. Manny Thanks
Erik
In the mongodb docs you can see that distinct returns null.
distinct(key[, query][, options], callback)
Arguments:
key (string) – key to run distinct against.
[query] (object) – option query to narrow the returned objects.
[options] (object) – additional options during update.
callback (function) – this will be called after executing this method.
The first parameter will contain the Error object if an error occured, or null otherwise.
While the second parameter will contain the results from distinct or null if an error occured.
**Returns: **
null
It takes a callback as the last argument. In this callback, the second argument contains the results you are looking for.
So your updated code would look like this:
function (callback) {
db.collection('stgOmniTracker')
.distinct("Responsible Group", function (err, group) {
if (err) return callback(err);
locals.group = group;
callback();
});
}
db.collection('stgOmniTracker').distinct("Responsible Group", function(err, result)
{
//your code here
});
I have an app.get that will return customer data and customer purchases. Inside this app.get I need run two mysql calls and build a an array to pass back.
How can I execute one query after another and process that data?
app.get('/customer', function (req,res) {
var response1 = [];
var response2 = [];
var processedData = [];
connection.query('QUERY HERE', function(err, rows, fields) {
if (!err){
response.push({rows});
} else {
res.status(400).send(err);
}
});
//for loop 'response' results and perform another query
for (var i = 0; i < response1.length; i++) {
var row = response1[i];
connection.query('QUERY HERE FOR row.customerid', function(err, rows, fields) {
if (!err){
processedData.push({'Customer Name:' : row.customername, 'purchases' : rows});
} else {
res.status(400).send(err);
}
});
}
//Send json back
res.setHeader('Content-Type', 'application/json');
res.status(200).send(JSON.stringify(processedData));
});
There is a very convenient module called async.js that provides a bunch of functions for doing complex async operations. Particularly,
async.waterfall() is great when you need to pass down results from one async operation/task to another.
async.mapSeries() is great when you need to create a new array with results from an array of async operation/tasks.
Let's use both.
If I understood your code correctly, the code would look something similar to
app.get('/customer', function (req, res) {
async.waterfall([
// each task is passed a callback 'cb' as last argument;
// you MUST call it at least and at most once within each task;
// if you pass an error into the callback as the first argument, it will stop the async function
function task1 (cb1) {
//connection.query('QUERY HERE', function(err, rows, fields) {
// if (err) return cb1(err); // stop waterfall() if an error occurred
// cb1(null, rows, fields); // pass results down to next task
//});
connection.query('QUERY HERE', cb1); // shorter version
},
function task2 (rows, fields, cb2) {
// iterate and run async operation over each element in array 'rows'
async.mapSeries(rows, function getPurchases (row, cb3) {
connection.query('QUERY HERE FOR row.customerid', function (err, purchases, fields) {
if (err) return cb3(err); // stop mapSeries() if an error occurred
cb3(null, { 'Customer Name': row.customername, 'purchases': purchases })
});
}, function (err, customers) {
// when mapSeries() is done iterating OR if an error occurred, it will come here
if (err) return cb2(err); // stop waterfall() if an error occurred
cb2(null, customers)
});
// }, cb2); // shorter version
}
], function (err, customers) {
// when waterfall() is done all its tasks OR if an error occurred, it will come here
// handle error and send response here
});
});
I want to save 8 objects to a MongoDB database using Mongoose. When the last document is saved, I want to report (i.e. send an event) that all documents have been saved.
The way I'm doing it now is quite messy (especially for increasingly larger amounts of documents I want to save).
This is how I have it now (just for 4 people for this example). Is there a cleaner way you can recommend?
person1.save(function(err, result){
if (err) console.log(err);
else{
person2.save(function(err, result){
if (err) console.log(err);
else{
person3.save(function(err, result){
if (err) console.log(err);
else{
person4.save(function(err, result){
if (err) console.log(err);
else{
done();
}
});
}
});
}
});
}
});
A useful library to coordinate asynchronous operations is async. In your case, the code would look something like this:
var people = [ person1, person2, person3, person4, ... ];
async.eachSeries(people, function(person, asyncdone) {
person.save(asyncdone);
}, function(err) {
if (err) return console.log(err);
done(); // or `done(err)` if you want the pass the error up
});
Using promises and Array.map()
const userPromises = persons.map(user => {
return new Promise((resolve, reject) => {
person.save((error, result) => {
if (error) {
reject(error)
}
resolve(result);
})
})
});
Promise.all(userPromises).then((results) => {
//yay!
//results = [first, second, etc...]
}, (error) => {
//nay!
})
I would recommend to have an array and save with iteration. Will have same performance but code would be cleaner.
You can have
var Person = mongoose.model('Person');
var people = [];
people[0] = new Person({id: 'someid'});
people[0].set('name', 'Mario');
people[1] = new Person({id: 'someid'});
people[1].set('name', 'Mario');
people[2] = new Person({id: 'someid'});
people[2].set('name', 'Mario');
var errors = [];
for(person in people){
people[person].save(function(err, done){
if(err) errors.push(err);
if (person === people.length){ yourCallbackFunction(errors){
if (errors.length!=0) console.log(errors);
//yourcode here
};
}
});
}
The best way is to use async waterfall.
Part of the code snippet might look as below. Please refer the above link.
It breaks the async nature and converts into a one after another process (if I am not wrong).
waterfall([
function(callback){
callback(null, 'one', 'two');
},
function(arg1, arg2, callback){
callback(null, 'three');
},
function(arg1, callback){
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
I would use Underscore Each to iterate over an array of People models. This would lead to your code being cleaner and help avoid the "boomerang effect" you are experiencing with your code.
From the documentation:
.each(list, iteratee, [context]) Alias: forEach
Iterates over a list of elements, yielding each in turn to an iteratee function. The iteratee is bound to the context object, if one is passed. Each invocation of iteratee is called with three arguments: (element, index, list). If list is a JavaScript object, iteratee's arguments will be (value, key, list). Returns the list for chaining.
For example:
var people = [person1, person2];
var length = people.length;
var hasError = false;
var doSomething = function() {
console.log("I'm done");
}
var doSomethingElse = function() {
console.log('There was an error');
}
_.each(people, function(person, i) {
if (!hasError) {
person.save(function(err, result) {
if (err) {
console.log(err);
hasError = true;
}
);
if (i === length) {
doSomething();
}
} else {
// There was an error
doSomethingElse();
}
});
I'm trying to add some mocha testing to a node module I have, but I'm new to it, and my lack of concreteness in terms of callbacks is hampering me.
I have tried to pare things back to the most straightforward example, but it's still not working.
So my main.js is
var async = require('async');
var myObject = {};
myObject.test = function(params) {
async.waterfall([
async.apply(test, params)
],
function(err, result){
if (err) {
console.log('Error: ' + err);
} else {
if (result === 200) {
return result;
}
}
});
};
function test(params, callback) {
if(params) {
callback(null, 200);
}
}
module.exports = myObject;
Then my test file
var assert = require("assert");
var myObject = require('./main');
describe('test', function(){
it("should return 200", function(done){
myObject.test({test: 'such test'}, function(err, res) {
if (err) return done(err);
assert.equal(res, 200);
done(res);
});
})
});
If I just run mocha it times out so I'm suspicious about that! Trying mocha --timeout 15000 also just stalls. Any direction you can provide would be really appreciated!
I got this far using this answer but can't get any further.
OK, I think I sorted it, but would still appreciate some feedback to see if I'm approaching it correctly, rather than just managing to get my test to pass.
var async = require('async');
var myObject = {};
myObject.test = function(params, callback) {
async.waterfall([
async.apply(test, params)
],
function(err, result){
if (err) {
console.log('Error: ' + err);
} else {
if (result === 200) {
callback(result);
}
}
});
};
function test(params, callback) {
if(params) {
callback(null, 200);
}
}
module.exports = myObject;
and the test file is
var assert = require("assert");
var myObject = require('./main');
describe('test', function(){
it("should return 200", function(done){
myObject.test({test: 'such test'}, function(res) {
assert.equal(res, 200);
done();
});
})
});
You fixed your main issue but your code still is broken. When you have an async method that takes a callback, you must always invoke the callback exactly once in all cases or your program's control flow will break. If you write an if/else clause, both branches must invoke the callback function. Both of your if statements above violate the callback contract. Check out understanding error-first-callbacks from The Node Way for a good explanation.
I am trying to learn node and understand how callbacks are working. I am doing this by trying to use the async lib. I am trying to hit my db with 2 separate calls and use the async lib to let me know when my object is ready to build. Here is my aysnc code:
async.parallel({
one: function(callback) {
getUser(id, callback);
},
two: function(callback) {
getUserServices(id, callback);
}
}, function(err, results) {
if(err) {
console.log(err);
new Error();
}
res.json(result);
});
Here are what my functions look like where I am calling the db. There are both basically the same function:
var getUser = function(id, callback) {
var query = client.query('SELECT * FROM USERS WHERE USER_ID=$1', [id]);
query.on('row', function(row, result) {
result.addRow(row);
});
query.on('end', function(result) {
callback(result);
});
};
My code hits the db and returns the user, but when it goes back up to the async code the user is in the err object. What am I doing wrong? How do I properly set up callbacks?
As pointed by damphat, your code should be
//additionally, handle errors
query.on('error', function(err){
callback(err) // The first argument to callback should be an error object
})
query.on('end', function(result){
callback(null, result) //passing null error object
})