Let's start with the general rule for using promises:
Every function that does something asynchronous must return a promise
Which functions are these in your case? It's getPrayerInCat
, the forEach
callback, and Prayer.find
.
Hm, Prayer.find
doesn't return a promise, and it's a library function so we cannot modify it. Rule 2 comes into play:
Create an immediate wrapper for every function that doesn't
In our case that's easy with Q's node-interfacing helpers:
var find = Q.nbind(Prayer.find, Prayer);
Now we have only promises around, and do no more need any deferreds. Third rule comes into play:
Everything that does something with an async result goes into a .then
callback
…and returns the result. Hell, that result can even be a promise if "something" was asynchronous! With this, we can write the complete callback function:
function getPrayerCount(data2) {
var id = data2.id;
return find({prayerCat:id})
// ^^^^^^ Rule 1
.then(function(prayer) {
// ^^^^^ Rule 3
if (!prayer)
data2.prayersCount = 0;
else
data2.prayersCount = prayer.length;
return data2;
// ^^^^^^ Rule 3b
});
}
Now, we have something a bit more complicated: a loop. Repeatedly calling getPrayerCount()
will get us multiple promises, whose asynchronous tasks run in parallel and resolve in unknown order. We want to wait for all of them - i.e. get a promise that resolves with all results when each of the tasks has finished.
For such complicated tasks, don't try to come up with your own solution:
Check the API of your library
And there we find Q.all
, which does exactly this. Writing getPrayerInCat
is a breeze now:
function getPrayerInCat(data) {
var promises = data.map(getPrayerCount); // don't use forEach, we get something back
return Q.all(promises);
// ^^^^^^ Rule 1
}
If we needed to do anything with the array that Q.all
resolves to, just apply Rule 3.