It is necessary to send a response after forEach is executed
I am working with NodeJS + Mongoose and I am trying to populate an array of objects and then send it to the client, but I cannot do that, the response is always empty because it is sent before the end of the forEach.
router.get('/', isAuthenticated, function(req, res) {
Order.find({ seller: req.session.passport.user }, function(err, orders) {
//handle error
var response = [];
orders.forEach(function(doc) {
doc.populate('customer', function(err, order) {
//handle error
response.push(order);
});
});
res.json(response);
});
});
Is there a way to send it after the loop completes?
source to share
Basically, you can use any asynchronous control flow solution like async or promises (see laggingreflex answer ), but I would recommend that you use Mongoose specialized methods to populate the entire array in a single MongoDB request.
The simplest solution is to use Query#populate
method to get already completed documents:
Order.find({
seller: req.session.passport.user
}).populate('customer').exec(function(err, orders) {
//handle error
res.json(orders);
});
But if for some reason you cannot use this method, you can call Model.populate
method yourself to populate an array of already loaded documents:
Order.populate(orders, [{
path: 'customer'
}], function(err, populated) {
// ...
});
source to share
One solution is to use Promises.
var Promise = require('bluebird');
Promise.promisifyAll(Order);
router.get('/', isAuthenticated, function(req, res) {
Order.findAsync({ seller: req.session.passport.user })
.then(function(orders) {
return Promise.all(orders.map(function(doc){
return Promise.promisify(doc.populate).bind(doc)('customer');
}));
}).then(function(orders){
// You might also wanna convert them to JSON
orders = orders.map(function(doc){ return doc.toJSON() });
res.json(orders);
}).catch(function(err){
//handle error
});
});
BlueBird .promisifyAll
creates a …Async
version of all the object's functions, saving you an extra step in setting up the initial promise. So instead Order.find
I used Order.findAsync
in the above example
source to share