Promises with Azure Mobile Services

Recently I decided to see if I could use JavaScript Promises in my Azure Mobile Services; doing so would allow me to clean up the services and reduce occurrences of the “Pyramid of Doom” anti-pattern.

In order to achieve this goal, I decided to bring the tried and true “Q” library (https://github.com/kriskowal/q) into my Azure Mobile Service.  This is accomplished by taking advantage of the Git repository Microsoft provides to every mobile service and running npm install Q from the command line within that repository (assuming you have node.js installed).

image

Doing this creates the node_modules directory which will contain the source JavaScript for external modules.  Now to actually use Q in your script add a require call to the top of your script.

var Q = require("q");

In reality, you can name the return variable whatever you like, however, I have found that most often you will see it referenced as a capital Q.

Without getting too much into the mechanism of how Promises work under the hood, the best way to approach using them in Mobile Service is to use the “deferred” approach.  This is a similar concept to jQuery, but slight different.  Consider the following method written without promises:

exports.get = function(request, response) {
    var userId = request.user.userId;
    var sql = "";

   request.service.mssql.queryRaw(sql, [userId], {
      success: function(result) {
         request.respond(200, []);
      }
   });
};

The problem here is that when you rely on callbacks you create a stack whereby each call must finish before the next one can execute.  Its not efficient and it gets messy.  Callbacks are fine for single level, but quickly spiral out of control.

For example, lets say the above code returned an identifier that three subsequent queries would use.  We would end up with 4 levels of this, hence the pyramid of doom.  This is not what we want.  We want to be able to execute our first query get the data and then execute the next three concurrently, returning once all complete.  Here is what this looks like with Promises:

var Q = require("q");

exports.get = function(request, response) {
   var userId = request.user.userId;
    var weekOf = request.query.weekForDate;
    
    // make a query to get the total points for the user for the week
    var weekDataPromise = getWeekFor(weekOf);

    weekDataPromise.then(function(weekInfo) {
      var pointsPromise = getTotalPoints(userId, weekInfo.weekId);
       var totalPredictionsPromise = getTotalPredictions(userId, weekInfo.weekId);
       var rankingPromise = getRanking(userId, weekInfo.weekId);
      var weekInfoPromise = Q(weekInfo);
        
      return Q.all([pointsPromise, totalPredictionsPromise, weekInfoPromise, rankingPromise])
         .then(function(results) {
            request.respond(200, results);
         });
    });
};

Pretty slick right?  Once we get the information for the week passed, we are able to pass it to our other three queries.  Each function houses the query and returns a promise.  These promises are then passed into all indicating the code should wait for all to complete before invoking the response handler.

Here is an example of two of these methods:

   function getWeekFor(weekOf) {
      var defer = Q.defer();
      var sql = "";

      request.service.mssql.queryRaw(sql, [weekOf], {
         success: function(result) {
            if (result.rows.length == 0) {
               defer.reject(new Error("No Week for WeekFor"));
            }
            else {
               defer.resolve({
                  weekNumber: result.rows[0][0],
                  year: result.rows[0][1], 
                  weekId: result.rows[0][2]
               });
            }
         }
      });

      return defer.promise;
   }

   function getTotalPoints(userId, weekId) {
      var defer = Q.defer();
      var sql = "";

      request.service.mssql.queryRaw(sql, [weekId, userId], {
         success: function(result) {
            if (result.rows.length == 0) {
               defer.resolve({
                  totalPoints: 0,
                  userId: userId,
                  weekId: weekId
               });
            }
            else {
               defer.resolve({
                  totalPoints: result.rows[0][0],
                  userId: userId,
                  weekId: weekId
               });
            }
         }
      });

      return defer.promise;
   }

The important concept here is the “deferred”.  This object allows us to manually complete the promise in the matter we see fit.  There are two calls here: resolve (success), and reject (failure).  You see the calls above to resolve which allow me to pass data back; we will cover how to access this data.  Reject rejects the promises and, in this case, would invoke the “fail” handle for all, since “all” did not resolve.

As for accessing the data, you will pull it out of the “results” variable in “all” calls then function.  In this case, since we have 4 promises being returned, results will be array of length 4.  0 = result of pointsPromise, 1 = result of totalPredictionsPromise, 2 = result of rankingPromise, and 3 = result of weekInfoPromise, which is the value that was returned from the initial promise that set up the other three.

So that’s it.  Using Promises is definitely an approach all developers should be looking at.  They give JavaScript some great syntactical features for handling asynchronous conditions and clean up code tremendously.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s