Utilizing Promises in SharePoint 2013 Apps 

Tags: REST and CSOM, SharePoint 2013

Before starting my discssuion of promises, I want to give a shout out to Rob Bogue who hates JavaScript, but was instrumental in pushing me down the promises pattern path.

 

This turned out to be a pretty big hit at the SharePoint Conference, so I wanted to write up a little summary of promises in SharePoint 2013 apps.

Rob also has some additional information on promises in his TypeScript write up.

***

When developing more complex apps against the SharePoint 2013 REST API, you must often make multiple asynchronous RESTful calls. For example, an initial RESTful call might retrieve master data while subsequest calls fill in child data. The challenge with multiple calls is that they need to happen sequentially, but each call is made asynchronously. So the only solution is to nest dependent calls within the “success” callback functions. The following code shows an example of nested RESTful calls where the first call retrieves the current user’s account name and the nested call returns the social feed for the account name.

"use strict";

var Wingtip = window.Wingtip || {};

Wingtip.FeedViewModel = function () {

    var init = function () {

        //Get the current user's account information
        $.ajax({
                    url: _spPageContextInfo.webServerRelativeUrl +
                        "/_api/SP.UserProfiles.PeopleManager/GetMyProperties",
                    method: "GET",
                    headers: {
                        "accept": "application/json;odata=verbose",
                    },
                    success: function (data) {

                        //Now get the current user's social feed
                        var accountName = data.d.AccountName;

                        $.ajax({
                                url: _spPageContextInfo.webServerRelativeUrl +
                                     "/_api/social.feed/actor(item='" +
                                     accountName +"')/Feed",
                                method: "GET",
                                headers: {
                                    "accept": "application/json;odata=verbose",
                                },
                                success: function (data) {
                                    var feedData = data.d;
                                },
                                error: function (err) {
                                    alert(JSON.stringify(err));
                                }
                            }
                         );
                    },
                    error: function (err) {
                        alert(JSON.stringify(err));
                    }
                }
            );
        };


    return {
        init: init
    }
}();

The challenge with the code above is that is can rapidly become unmaintainable. Multiple levels of nested asynchronous calls simply creates a pile of “spaghetti” code. What is needed is a mechanism to separate out the various asynchronous calls while still maintaining the dependency between them. That is the function of a promise.

A promise - also known as a deferred - is a pattern that returns an object immediately from an asynchronous call. This object will later be populated with the result of the asynchronous call, but its immediate return simplifies the code structure making it much more maintainable. Furthermore, promises provide a built-in caching mechanism so that the same query does not have to be run again if the promise has already been successfully fulfilled.

There are several techniques for implementing promises, but one of the easiest is to make use of the jQuery $.Deferred method. The $.Deferred method allows you to create a deferred, which can be immediately returned from an asynchronous call. The deferred object has resolve and reject methods, which are called on success or failure respectively. Using deferreds allows you to separate the JavaScript code that performs the asynchronous call. The following code rewrites the initial code listing into two separate libraries that implement the promise pattern.

"use strict";

var Wingtip = window.Wingtip || {};

Wingtip.ProfileQuery = function () {

    var deferred = $.Deferred(),

        execute = function () {

        $.ajax(
                {
                    url: _spPageContextInfo.webServerRelativeUrl +
                        "/_api/SP.UserProfiles.PeopleManager/GetMyProperties",
                    method: "GET",
                    headers: {
                        "accept": "application/json;odata=verbose",
                    },
                    success: function (data) {
                        deferred.resolve(data);
                    },
                    error: function (err) {
                        deferred.reject(err);
                    }
                }
            );

        return deferred;
    };


    return {
        execute: execute
    }

}();

Wingtip.FeedQuery = function () {

    var deferred = $.Deferred(),

        execute = function (accountName) {

            $.ajax(
                    {
                        url: _spPageContextInfo.webServerRelativeUrl +
                            "/_api/social.feed/actor(item='" + accountName + "')/Feed",
                        method: "GET",
                        headers: {
                            "accept": "application/json;odata=verbose",
                        },
                        success: function (data) {
                            deferred.resolve(data);
                        },
                        error: function (err) {
                            deferred.reject(err);
                        }
                    }
                );

            return deferred;
        };


    return {
        execute: execute
    }

}();

Notice in the rewritten code how the two libraries follow the same pattern. The deferred is returned immediately and the resolve/reject methods are called for success and failure.

The deferred object exposes a “promise” object that has a method named “then”. The “then” method takes two arguments: the first argument is a success function, the second is a failure function. So the library above can be invoked easily using the following code.

Wingtip.FeedViewModel = function () {

    Wingtip.ProfileQuery.execute().promise().then(

        //success
        function (data) {

            Wingtip.FeedQuery.execute(data.d.AccountName).promise().then(

                //success
                function (data) {
                },

                //failure
                function (err) {
                }
            );

        },

        //failure
        function(err) {
        }
       
    );

}

The promises pattern significantly simplifies JavaScript code when you app must make multiple, nested asynchronous calls and that makes it very powerful. However, the promise object also acts like a caching mechanism in that the success or failure function will be called immediately if the promise has already been fulfilled. This opens up additional ideas such as creating arrays of promises that contain fulfilled data so that apps do not have to run queries that have already successfully executed.

 

Update: Note that the $.ajax method returns a promise, so there's no need to explicitly return a promise unless you want to extend it with your own extra information.

 
Posted by Scot Hillier on 29-Nov-12
0 Comments  |  Trackback Url  |  Link to this post | Bookmark this post with:        
 

Links to this post

Comments