I'm trying to find a way to send multiple requests (using Express) and process all the response in one function.
Here's my code :
// In router.js
app.get('/api/FIRST_PATH', CALLBACK_FUNCTION_A );
// In CALLBACK_FUNCTION_A file :
module.exports = function (req, response) {
CALLBACK_FUNCTION_TO_SERVICE_A();
CALLBACK_FUNCTION_TO_SERVICE_B();
CALLBACK_FUNCTION_TO_SERVICE_C();
}
My problem is to send the requests CALLBACK_FUNCTION_TO_SERVICE_A, CALLBACK_FUNCTION_TO_SERVICE_B and CALLBACK_FUNCTION_TO_SERVICE_C and then retrieve all the results in another function to process them.
Any help would be greatly appreciated.
You can learn more about the new js standard and use Promise.
// In CALLBACK_FUNCTION_A file :
module.exports = function (req, response) {
var promises = [CALLBACK_FUNCTION_TO_SERVICE_A(),
CALLBACK_FUNCTION_TO_SERVICE_B(),
CALLBACK_FUNCTION_TO_SERVICE_C()];
Promise.all(promises).then( function(results) {
//results is an array
//results[0] contains the result of A, and so on
});
}
Of course CALLBACK_FUNCTION_TO_SERVICE_A() and such need to return Promise objects. You form a function like this:
function asyncFunction(callback) {
//...
callback(result);
}
You can create a Promise like this:
var p = new Promise(asyncFunction);
It'll start running the function, and supports the Promise interface.
So for example, either use request-promise or you can do something like:
function CALLBACK_FUNCTION_TO_SERVICE_A() {
var worker = function(callback) {
app.get('/api/FIRST_PATH', callback);
};
return new Promise(worker);
}
You can read more about Promise and how to also easily handle errors.
You could use async parallel. You can keep all your API calls as async.parallel array or JSON(Example uses Array).
async.parallel(
[
function(done){
reqServcieA(..., funnction(err, response){
if(err) done(err,null);
done(null, response);
}
},
function(done){
reqServcieA(..., funnction(err, response){
if(err) done(err,null);
done(null, response);
}
},
...// You can keep as many request inside the array
], function(err,results){
// Will be called when all requests are returned
//results is an array which will contain all responses as in request arry
//results[0] will have response from requestA and so on
});
Related
Let's say I have 3 files.
index.js makes a call to the backend like this
$.post('/test/', data, function(response) {
//handle success here
})
routes.js handles the route like this
app.post('/test/', function(req, res){
item.getItems(function(response){
res.json(response);
});
});
items.js is the model which accesses the database and makes a POST request for each item
function getItems(callback) {
database.query('SELECT * from items', function(result){
result.forEach(function(item){
request.post('/api/', item, function(req, res) {
//finished posting item
});
});
});
//callback here doesnt wait for calls to finish
}
where/when should I call the callback passed to getItems() to handle a success/failure in index.js?
Because your request.post() operations are async, you have to use some method of keeping track of when they are all done and then you can call your callback. There are multiple ways of doing that. I'll outline a few:
Manually Keeping Track of Count of Request Operations
function getItems(callback) {
database.query('SELECT * from items', function(result){
var remaining = result.length;
result.forEach(function(item){
request.post('/api/', item, function(err, res) {
--remaining;
//finished posting item
if (remaining === 0) {
callback();
}
});
});
});
}
The main problem with doing this manually is that propagating error in nested async operations is difficult when you get to actually figuring out how you're going to handle errors. This is much easier in the other methods shown here.
Using Promises
// load Bluebird promise library
var Promise = require('bluebird');
// promisify async operations
Promise.promisifyAll(request);
function queryAsync(query) {
return new Promise(function(resolve, reject) {
// this needs proper error handling from the database query
database.query('SELECT * from items', function(result){
resolve(result);
});
});
}
function getItems(callback) {
return queryAsync('SELECT * from items').then(function(result) {
return Promise.map(result, function(item) {
return request.postAsync('/api/', item);
});
});
}
getItems.then(function(results) {
// success here
}, function(err) {
// error here
})
It seems strange that you're making an API request in your server-side code, unless this is some sort of middle tier code that interacts with the API... but you're interacting with a database, so I'm still confused on why you can't just do a database insert, or have a bulk insert API call?
Anyway, if you must do it the way you're asking, I've done this in the past with a recursive method that trims down the result array... I really don't know if this is a good approach, so I'd like to hear any feedback. Something like this:
function recursiveResult(result, successfulResults, callback) {
var item = result.shift();
// if item is undefined, then we've hit the end of the array, so we'll call the original callback
if (item !== undefined) {
console.log(item, result);
// do the POST in here, and in its callback, call recursiveResult(result, successfulResults, callback);
successfulResults.push(item);
return recursiveResult(result, successfulResults, callback);
}
// make sure callback is defined, otherwise, server will crash
else if (callback) {
return callback(successfulResults);
}
else {
// log error... callback undefined
}
}
function getItems(callback) {
var successfulResults = [];
var result = [1, 2, 3, 4];
recursiveResult(result, successfulResults, callback);
}
console.log('starting');
getItems(function(finalResult) {
console.log('done', finalResult);
});
So I have sails app with some help service to make it easier to create and get complex models.
one of theses are
getMerits: function(profileId, limit){
return async.waterfall([
function(callback){
Merit.find({employeeProfile: profileId}).then(function(merits){
callback(null, merits);
});
},
function(merits, callback){
async.forEach(merits, function(item, loop_callback){
MeritIndex.findOne({id: item.index}).then(function(meritIndex){
merits[merits.indexOf(item)].index = meritIndex;
loop_callback();
});
}, function(err, results){
callback(null, merits);
});
}
], function(err, results){
return results;
});
}
the trouble is when I try to call this function to get the result(list of merits with their meritindexes inserted.) I cant figure out the correct way to get the results returned from the async waterfall:
async.forEach(profiles, function(item, loop_callback){
MeritService.getMerits(item.id, 5).exec(function(err, merits){
console.log(merits)
profiles[profiles.indexOf(item)].merits = merits;
loop_callback();
});
// MeritService.getMerits(item.id, 5).exec(function(m){
// console.log(m)
// profiles[profiles.indexOf(item)].merits = m;
// loop_callback();
// });
}, function(err){
console.log("PROFILES" + JSON.stringify(profiles))
});
the print of merits here results in undefined. Is there any way to treat async waterfall as a promise and use then instead of exec?
You don't need to use async.waterfall since you already have promises, promises chain already - so adding another library for that logic is redundant. Waterline uses bluebird promises which come with convenience methods already.
Your getMerits can be written as:
getMerits: function(profileId, limit){
var merits = Merit.find({employeeProfile: profileId});
var items = merits.map(function(item) {
return MeritIndex.findOne({id: item.index }).then(function(meritIndex) {
item.index = meritIndex;
});
});
return items.return(merits); // wait for items to be done, and return the merits
}
P.S.
If you use Node 4+ let me know since it gets even simpler.
I'm trying to test my REST API endpoint handlers using Mocha and Chai, the application was built using Express and Mongoose. My handlers are mostly of the form:
var handler = function (req, res, next) {
// Process the request, prepare the variables
// Call a Mongoose function
Model.operation({'search': 'items'}, function(err, results) {
// Process the results, send call next(err) if necessary
// Return the object or objects
return res.send(results)
}
}
For example:
auth.getUser = function (req, res, next) {
// Find the requested user
User.findById(req.params.id, function (err, user) {
// If there is an error, cascade down
if (err) {
return next(err);
}
// If the user was not found, return 404
else if (!user) {
return res.status(404).send('The user could not be found');
}
// If the user was found
else {
// Remove the password
user = user.toObject();
delete user.password;
// If the user is not the authenticated user, remove the email
if (!(req.isAuthenticated() && (req.user.username === user.username))) {
delete user.email;
}
// Return the user
return res.send(user);
}
});
};
The problem with this is that the function returns as it calls the Mongoose method and test cases like this:
it('Should create a user', function () {
auth.createUser(request, response);
var data = JSON.parse(response._getData());
data.username.should.equal('some_user');
});
never pass as the function is returning before doing anything. Mongoose is mocked using Mockgoose and the request and response objects are mocked with Express-Mocks-HTTP.
While using superagent and other request libraries is fairly common, I would prefer to test the functions in isolation, instead of testing the whole framework.
Is there a way to make the test wait before evaluating the should statements without changing the code I'm testing to return promises?
You should use an asynchronous version of the test, by providing a function with a done argument to it.
For more details refer to http://mochajs.org/#asynchronous-code.
Since you don't want to modify your code, one way to do that could be by using setTimeout in the test to wait before to call done.
I would try something like this:
it('Should create a user', function (done) {
auth.createUser(request, response);
setTimeout(function(){
var data = JSON.parse(response._getData());
data.username.should.equal('some_user');
done();
}, 1000); // waiting one second to perform the test
});
(There might be better way)
Apparently, express-mocks-http was abandoned a while ago and the new code is under node-mocks-http. Using this new library it is possible to do what I was asking for using events. It's not documented but looking at the code you can figure it out.
When creating the response object you have to pass the EventEmitter object:
var EventEmitter = require('events').EventEmitter;
var response = NodeMocks.createResponse({eventEmitter: EventEmitter});
Then, on the test, you add a listener to the event 'end' or 'send' as both of them are triggered when the call to res.send. 'end' covers more than 'send', in case you have calls other than res.send (for example, res.status(404).end().
The test would look something like this:
it('Should return the user after creation', function (done) {
auth.createUser(request, response);
response.on('send', function () {
var data = response._getData();
data.username.should.equal('someone');
data.email.should.equal('asdf2#asdf.com');
done();
});
});
I am having my code as
function updateData(data){
data.forEach(function(obj){
users.find({_id:obj.userId}).toArray(
function(e, res) {
obj.userData = res;
console.log(res)
});
});
return data;
}
The problem is I am unable to find the updated data, I am trying to update my data and adding one more field to it based on userId. The data parameter is an array containing the output from comments table. hope you understand the scenario.
It looks that users.find({...}).toArray(function(...){...}) is going to be asynchronous, so there is no way that you can be sure that the db call has been completed and that each data.obj has been updated before data is returned.
Instead of using the javascript Array.prototype.forEach() function, you could use the NodeJS async.each function from the async library by Caolan which would iterate through the array, update each object and then return the data object only when all functions calls have completed.
For example:
var async = require("async");
function updateData(data){
async.each(data, function(obj, callback) {
users.find({_id:obj.userId}).toArray(
function(e, res) {
obj.userData = res;
callback(e);
}
);
},
function(error){
return data;
}
}
Im using express.js to create a node.js REST server, as a part o this i am also creating a simple session system. I have 3 modules:
app.js
highscoreMan.js
userSession.js
The app.js url for http://localhost/api/highscores now calls userSession with given parameters:
//Get all highscores
app.get('/api/highscores', function (req, res) {
userSession.checkValidity(req.query['username'], req.query['sessionid'], highscoreMan.getAll(req, res));
});
However, in checkValidity the function that i pass is automatically called:
function checkValidity(username, sessionId, callback) {
userSession.findOne({ userid: username, sessionid: sessionId }, function (err, result) {
if (err) {
console.log(err);
}
if(result) {
callback;
}
});
}
I only want to run the function being passed given that i get the proper results from the database(other checks will be added later for session dates etc.). How would i do this?
To delay calling highscoreMan.getAll(), you'll need to make it a statement of another function that can be called later:
app.get('/api/highscores', function (req, res) {
userSession.checkValidity(req.query['username'], req.query['sessionid'], function () {
highscoreMan.getAll(req, res);
});
});
Otherwise, it's being called immediately and its return value is instead being passed to userSession.checkValidity().
Note that you'll also need to adjust checkValidity to call the passed callback:
// ...
if(result) {
callback();
}
// ...
Unless I don't fully understand your problem, couldn't you just do something like this?
if (result && some_validator(result)) {
callback();
}