4

I'm building a site with node/express/mongoose and it needs to do the following things when viewing a submission.

The problem I'm running into is doing db fetches in a non-serial fashion. For example, I'll do a few calls to fetch some data, but some of the calls might not finish until the execution context goes to the other. Tried to use the npm module, async, but am having trouble trying to figure out how I would integrate it.

Here is my code:

var getViewCount = function(submissionId) {
  Submission.getSubmissionViewCount({
    submissionId : submissionId
  }, function(err, count) {
    if (err) {
      throw err;
    }

    if (count) {
      return count;
    }
  });
};

var getVotes = function(submissionId) {
  console.log('getvotes');
  Submission.getSubmissionVotes({
    submissionId : submissionId
  }, function(err, votes) {
    return votes;
  });
};

var getSubmission = function(id) {
  Submission.getSubmission({
    id : id
  }, function(err, submission) {
    if (err) {
      throw err;
    }

    if (submission) {
      return submission;
    }
  });
};

var renderSubmission = function(title, submission, views) {
  res.render('submission', {
      title: submission.title + ' -',
      submission: submission,
      views: views.length
  });
};

How do I use this with async? Or should I be using async.series isntead of async.async?

async.series([
  function(callback) {
    var submission = getSubmission(id);
    callback(null, submission);
  },
  function(callback) {
  //  getViewCount(submissionId);
  },
  function(callback) {
//    getVotes(submissionId);
  },
  function(callback) {
    //renderSubmission(title, submission, views);
  }
  ], function(err, results) {
    console.log(results);
  });

Basically I want to fetch the views and votes first and then render my submission.

bob_cobb
  • 2,229
  • 11
  • 49
  • 109
  • 1
    the return in your `getSubmission` function, for example, doesn't return anything from the callback of `Submission.getSubmission`. You need along the submissionId argument to also give it a callback and inside the db callback instead of `return submission` have it `callback(submission)` – TheBrain Mar 22 '13 at 09:39
  • 1
    Have you looked at async's queue method? The general idea is what @TheBrain and RobertMitchell have suggested. callback functions are key. In the past, I have used the queue, drain and forEach (for the list of db calls) methods with good success to make async calls to MS SQL server. – DotNetWala Mar 22 '13 at 15:38
  • 1
    Try to look into promise based solutions: http://stackoverflow.com/questions/11912573/node-js-deferred-promisify-mongoose/11931908#11931908 http://stackoverflow.com/questions/10545087/how-to-use-q-module-for-refactoring-mongoose-code/10554943#10554943 – Mariusz Nowak Mar 22 '13 at 22:21
  • @TheBrain good call thanks. – bob_cobb Mar 24 '13 at 07:31

1 Answers1

2

TheBrain's description of the overall structural changes that you should make to your code is accurate. The basic methodology in Node is to nest a series of callbacks; very rarely should you require functions that actually return a value. Instead, you define a function that takes a callback as parameter and pass the result into that callback. Please review the code below for clarification (where cb is a callback function):

var getViewCount = function(submissionId, cb) {
    Submission.getSubmissionViewCount({
        submissionId : submissionId
    }, function(err, count) {
        if (err) {
            throw err;
        }

        if (cb) {
            cb(count);
        }
    });
};

var getVotes = function(submissionId, cb) {
    console.log('getvotes');
    Submission.getSubmissionVotes({
        submissionId : submissionId
    }, function(err, votes) {
        if (cb) {
            cb(votes);
        }
    });
};

var getSubmission = function(id, cb) {
    Submission.getSubmission({
        id : id
    }, function(err, submission) {
        if (err) {
            throw err;
        }

        if (cb) {
            cb(submission);
        }
    });
};

var renderSubmission = function(submissionId) {

    getSubmission(submissionId, function (submission) {
        if (!submission) {
            // unable to find submission
            // add proper error handling here
        } else {
            getViewCount(submissionId, function (viewCount) {
                res.render('submission', {
                    title: submission.title + ' -',
                    submission: submission,
                    views: viewCount
                });
            });
        }
    };
};
Robert Mitchell
  • 1,334
  • 8
  • 10
  • Ah yeah this callback soup gets pretty sloppy after awhile, but throwing callbacks as the last param works fine. I'll stick with this until I find a better method. Thanks. – bob_cobb Mar 24 '13 at 07:31