SharePoint: Using deferreds/Promises Or Callbacks in Javascript client object model

We know that SharePoint JavaScript client object model is asynchronous. However, sometimes we want to process things in synchronous way. This can be done by using JavaScript call backs and deferreds/Promises. Let's first see an asynchronous example which we will later convert to synchronous:
<script type="text/javascript">
    var camlItems;
    $(document).ready(function () {
        //don't exectute any jsom until sp.js file has loaded.        
        SP.SOD.executeFunc('sp.js', 'SP.ClientContext', prepareTables);
    });

    function prepareTables() {
        getItemsWithCaml('External User Account Request');
        console.log('Completed table prparation.');                
    }

    function getItemsWithCaml(listTitle) {
        var clientContext = new SP.ClientContext.get_current();
        var list = clientContext.get_web().get_lists().getByTitle(listTitle);
        var camlQuery = new SP.CamlQuery();
        camlItems = list.getItems(camlQuery);
        clientContext.load(camlItems);
        clientContext.executeQueryAsync(
            Function.createDelegate(this, this.onQuerySucceeded),
            Function.createDelegate(this, this.onQueryFailed)
        );
    };

    function onQuerySucceeded(sender, args) {
        var listItemEnumerator = camlItems.getEnumerator();
        while (listItemEnumerator.moveNext()) {
            var listItem = listItemEnumerator.get_current();
            console.log(listItem.get_item('Title'));
        }
    }

    function onQueryFailed(sender, args) {
        console.log('An error occured while retrieving list items:' + args.get_message());
    }
</script>
The External User Account Request list has two items in it. If I press F12 and check the log, I see the following output:

Console Output

As is clear the from the output, the code execution moves immediately to line console.log('Completed table prparation.');. It doesn't wait for method getItemsWithCaml(listTitle) to complete.
Now given below is an example using call backs. 
$(document).ready(function () {
        //don't exectute any jsom until sp.js file has loaded.        
        SP.SOD.executeFunc('sp.js', 'SP.ClientContext', prepareTables);
    });

function prepareTables() {
    getItemsWithCaml('External User Account Request',
            function (camlItems) {
                var listItemEnumerator = camlItems.getEnumerator();
                while (listItemEnumerator.moveNext()) {
                    var listItem = listItemEnumerator.get_current();
                    console.log(listItem.get_item('Title'));
                } console.log('Completed table prparation.');
            },
            function (sender, args) {
                console.log('An error occured while retrieving list items:' + args.get_message());
            });
}    

function getItemsWithCaml(listTitle, success, error) {
    var clientContext = new SP.ClientContext.get_current();
    var list = clientContext.get_web().get_lists().getByTitle(listTitle);
    var camlQuery = new SP.CamlQuery();
    var camlItems = list.getItems(camlQuery);
    clientContext.load(camlItems);
    clientContext.executeQueryAsync(
            function () {
                success(camlItems);
            },
            error
        );
};
Here is the output of the above script.

Console Output

And here is same example using deferreds/Promises. It gives same output as callbacks.
$(document).ready(function () {
        //don't exectute any jsom until sp.js file has loaded.        
        SP.SOD.executeFunc('sp.js', 'SP.ClientContext', prepareTables);
    });    

function prepareTables() {
    getItemsWithCaml('External User Account Request').then(
            function (camlItems) {
                var listItemEnumerator = camlItems.getEnumerator();
                while (listItemEnumerator.moveNext()) {
                    var listItem = listItemEnumerator.get_current();
                    console.log(listItem.get_item('Title'));
                } console.log('Completed table prparation.');
            },
            function (sender, args) {
                console.log('An error occured while retrieving list items:' + args.get_message());
            }
        );   
}    

function getItemsWithCaml(listTitle) {
    //use of $.Deferred in the executeQueryAsync delegate allows the consumer of this method to write 'syncronous like' code
    var deferred = $.Deferred();
    var clientContext = new SP.ClientContext.get_current();
    var list = clientContext.get_web().get_lists().getByTitle(listTitle);
    var camlQuery = new SP.CamlQuery();        
    var items = list.getItems(camlQuery);
    clientContext.load(items);
    clientContext.executeQueryAsync(
        Function.createDelegate(this,
            function () { deferred.resolve(items); }),
        Function.createDelegate(this,
            function (sender, args) { deferred.reject(sender, args); }));

    return deferred.promise();
};
profile for Nadeem Yousuf at SharePoint Stack Exchange, Q&A for SharePoint enthusiasts

+