Your code is suffering from a misunderstanding of how async works. First and foremost you should read the Felix Kling link that @elclanrs posted here. SO contributors tend to get tired of answering the same async question over and over. I haven't quite gotten that tired yet, so I'll bite for the purposes of explaining async, but I'll also suggest another option that might be a much better way to solve your problem.
The async solution: There are many ways to await an array of asynchronous operations to complete. async.queue
is a popular choice. How it works is you push a set of pending operations into a queue and then tell that queue to wait until all results have been received, at which point you perform your res.send(). The code would look something like this:
var async = require('async');
Show.findOne({
'slug': req.params.slug
}, function(err, show) {
if (err) {
res.send(err);
}
var episodeArray = [];
var queue = async.queue(function(episodeID, callback) {
Episode.findById(episodeID, function(err, episode) {
if (err) {
throw err;
}
episodeArray.push(episode);
callback();
});
});
// Note that forEach is synchronous.
// The tasks will be pushed to the queue before drain()
episodes.forEach(function(episodeID, index) {
queue.push(episodeId);
});
queue.drain = function() {
res.send(episodeArray);
};
});
This is not the best way to solve your problem, this is only for the purpose of demonstrating how you could fix your existing code.
As long as your episodes array is not obscenely huge, then a far better way to query your episodes might be to use mongoDB's $in
operator, like so:
Show.findOne({
'slug': req.params.slug
}, function(err, show) {
if (err) {
res.send(err);
}
Episode.find({
_id: {
$in: show.episodes
}
}, function(err, episodes) {
if (err) {
throw err;
}
res.send(episodes);
});
});
Edit:
If you want to go a bit deeper, I should also mention that mongoose supports promises, which you can use to make your code less nested and repetitive, like this:
Show.findOne({
slug: req.params.slug
})
.then(function(show) {
return Episode.find({
_id: {
$in: show.episodes
}
});
})
.then(function(episodes) {
res.send(episodes);
})
// Any errors returned above will short circuit to the middleware next() here
.error(next);