Reputation: 928
I'm new to promises and wondering why the following is not working. I'm assuming because I need to set up my custom methods a specific way to work with Promises.
I'm using bluebird as my Promise library. I am trying to obtain a list of "discussions" objects given a "user's" username through use of mongoose statics and methods on these respective models (i.e., the Discussion and User models).
Below if my request handler in Express. Just trying to get the JSON and send it as the response for now:
/* /routes/users.js */
var Promise = require('bluebird');
var User = require('../models/').User
/* ... express inclusions, other routes ... */
/* Get list of discussions for user */
router.get('/:username/discussions', function(req, res) {
User.findOne({username: req.params.username}).then(function(user) {
if (user) {
return user.getDiscussions();
} else {
res.send('no user found');
}
}).then(function(discussions) {
res.send(discussions);
}).catch(function(error) {
console.log('ERROR: ' + error);
res.send('ERROR: ' + error);
});
});
Below is user.getDiscussions():
/* /models/user.js */
var Promise = require('bluebird');
var mongoose = Promise.promisifyAll(require('mongoose'));
var Discussion = require('./discussion').Discussion;
var Schema = mongoose.Schema;
/* ... UserSchema defined here ... */
UserSchema.methods.getDiscussions = Promise.method(function() {
var self = this;
self.model('User').findById(self.id).then(function(user) {
if (!user) {
Promise.reject('User \'' + self.id + '\' does not exist.');
} else {
return Discussion.getDiscussionsForUser(self.username);
}
});
});
And, finally, Discussion.getDiscussionsForUser:
/* /models/discussion.js */
var Promise = require('bluebird');
var mongoose = Promise.promisifyAll(require('mongoose'));
var Schema = mongoose.Schema;
/* ... DiscussionSchema defined here ... */
DiscussionSchema.statics.getDiscussionsForUser = Promise.method(function(username) {
var self = this;
self.model('User').findOne({username: username}, 'discussions').then(function(user) {
if (!user) {
return Promise.reject('User with username \'' + username + '\' does not exist.');
} else {
var discussions = [];
return self.model('Discussion').find({
'_id': { $in : user.discussions }
});
}
});
});
It seems that the request in the route is finishing early and sending undefined in the response. Any idea why the discussions obtained from Mongoose in the Discussion model aren't bubbling up to the route?
Upvotes: 1
Views: 293
Reputation: 707218
getDiscussions()
and getDiscussionsForUser()
are not properly returning a promise. Change them as follows:
UserSchema.methods.getDiscussions = Promise.method(function() {
var self = this;
// *** added a return to this next line
return self.model('User').findById(self.id).then(function(user) {
if (!user) {
// *** added a return to this next line
return Promise.reject('User \'' + self.id + '\' does not exist.');
} else {
return Discussion.getDiscussionsForUser(self.username);
}
});
});
Same with getDiscussionsForUser()
which should be this:
DiscussionSchema.statics.getDiscussionsForUser = Promise.method(function(username) {
var self = this;
// *** added a return to the next line
return self.model('User').findOne({username: username}, 'discussions').then(function(user) {
if (!user) {
return Promise.reject('User with username \'' + username + '\' does not exist.');
} else {
var discussions = [];
return self.model('Discussion').find({
'_id': { $in : user.discussions }
});
}
});
});
For a caller to be able to coordinate with the completion of an async operation within a function using promises, all code paths through the function must always return a promise from that function. Once you're inside a .then()
handler, you must return a promise if there are further nested async operations or you can just return a value if you want that value to become the value of the promise you are within.
Upvotes: 2