Reputation: 285
I want to make a synchronous call to one of the APIs that i have designed using loopback. Here is my code :
router.get('/friends', function(req, res, Array) {
var friendsList = []
for(var counter=0;counter<length;counter++){
Model.findById(Array[counter].Id,function(err, record) {
if(record){
friendsList.push(record);
}
});
}
res.status(200).json(friendsList);
});
Now im getting the response before the array, friendList is getting populated completely. I want to make this synchronous and get the correct response. What should i do?
Thanks!
Upvotes: 0
Views: 931
Reputation: 51
You should wait until the N findById calls finish so a solution is make an array of promises and use the Promise.all method.
router.get('/friends', function(req, res, Array) {
var friendsList = []
var friendsPromises = []
for (var counter=0; counter<length; counter++) {
var friend = Model.findById(Array[counter].Id;
friendsList.push(friend)
friend.then(function(record) {
if (record) {
friendsList.push(record);
}
})
}
Promise.all(friendsPromises).then(function(){
res.status(200).json(friendsList);
})
});
Upvotes: 0
Reputation: 195
I'd use async
and underscore
modules
var async = require('async');
var _ = require('underscore');
router.get('/friends', function(req, res, Array) {
var friendsList = [];
var waterfallFunctions = [];
_.each(Array, function(element) {
waterfallFunctions.push( function(next) {
Model.findById(element.Id,function(err, record) {
if (err) return next(err);
if(record){
friendsList.push(record);
}
next();
});
});
});
//async waterfall calls the functions in the array in order.
async.waterfall(waterfallFunctions, function (err) {
if (err) return res.status(500);
res.status(200).json(friendsList);
});
});
Upvotes: 1
Reputation: 1522
Using Promises and the $in
operator in Mongoose:
router.get('/friends', function(req, res, arr) {
var promise = Model.find({ _id: { $in: arr.slice(0, length).map(function (e) { return e.Id }) } }).exec()
promise.then(function (friendsList) {
res.status(200).json(friendsList)
})
})
arr.slice
picks the first length
elements from the array. Model.find
creates a single query, which is more efficient than performing multiple findById
queries. The $in
operator accepts an array of ids to do a look-up against. .exec()
creates a single promise, which is resolved with .then(...)
.
Upvotes: 0
Reputation: 1
a simplistic way to do it is
length
res.status...
Like this:
router.get('/friends', function(req, res, array) {
var friendsList = [];
var remaining = length;
for(var counter=0;counter<length;counter++){
Model.findById(array[counter].Id, function(err, record) {
if (record){
friendsList.push(record);
}
remaining -= 1;
if (remaining == 0) {
res.status(200).json(friendsList);
}
});
}
});
for completeness ... since you're using nodejs ... use (native) Promise and arrow function
syntax
router.get('/friends', (req, res, array) =>
Promise.all(array.map(item =>
new Promise((resolve, reject) =>
Model.findById(item.Id, (err, record) =>
resolve(record || undefined)
)
)
))
.then(friendsList =>
res.status(200).json(friendsList.filter(item =>
item !== undefined
))
)
);
Upvotes: 3