How to make toJSON wait for find before returning the object in Sails.js?

I'm running a find inside the following toJSON in the model but it return returns the object before the find completes. How can I wait until the find completes before firing the return?

toJSON: function() {
 var obj = this.toObject();
 Comment.find({
    postID: obj.id
 }).limit(2).sort('createdAt DESC').exec(function(err, comments) {
    obj.comments = comments; //this is not reflected in return obj
    if (obj.isTrue) {
        //yes
    } else {
        //no
    }
});
return obj; // obj.comments not reflected :(
}

The goal is for obj.comments to be in obj when it is returned.

That's because .exec takes a callback function (in your case function(err, comments){...}) which is asynchronously executed (i.e. it's executed somewhen later, in your case after return obj). This is a common problem for people new to JavaScript. You have two options:

  1. Make your toJSON function also take a callback (a callback function is a function that is passed to another function as an argument. That function in turn is then called a higher-order function):

    var toJSON = function(callback) {
       var obj = this.toObject();
       Comment.find({
          postID: obj.id
       }).limit(2).sort('createdAt DESC').exec(function(err, comments) {
          obj.comments = comments;
          if (obj.isTrue) {
              //yes
          } else {
              //no
          }
          callback(obj);
      });
    }
    

    usage:

    toJSON( function(obj) {
        //now obj is updated
    });
    
  2. Use a promise library like Q and have toJSON return a promise:

    var toJSON = function() {
       var deferred = Q.defer();
       var obj = this.toObject();
       Comment.find({
          postID: obj.id
       }).limit(2).sort('createdAt DESC').exec(function(err, comments) {
          obj.comments = comments;
          if (obj.isTrue) {
              //yes
          } else {
              //no
          }
          deferred.resolve(obj);
      });
      return deferred.promise;
    }
    

    usage:

    toJSON().then( function(obj) {
        //now obj is updated
    });
    

The solution ended up being to add an associate between posts and comments.

You can find documentation on that here: http://sailsjs.org/#/documentation/concepts/ORM/Associations

For example, in the post model add an attribute like so

comments: {
  collection: 'comment',
  via: 'postID'
},

And in the comments model add this attribute

postId: {
  model: 'hacks',
  type: 'STRING',
  required: true
},

when adding new documents set a comment's postId to the id of the post you'd like to associate. Then in the post controller when you are finding posts add populate like so

.populate('comments', {
  limit: 3,
  sort: 'createdAt DESC'
}).exec(...