How to Make SharePoint JSOM executeQueryAsync Synchronous using Promises

Introduction

The SharePoint 2013 Client Object Model allows developers to retrieve, update, and manage data from SharePoint lists and libraries. Among various APIs available, two major client-side options are:

  • JavaScript Object Model (JSOM)
  • REST/OData Endpoints

In this article, we'll focus on JSOM. By default, JSOM operations like executeQueryAsync() are asynchronous, which means JavaScript does not wait for these operations to complete. This can lead to unexpected behavior when you try to execute code sequentially.

To solve this issue and make executeQueryAsync() behave like a synchronous call (i.e., wait until it completes), we can use JavaScript Promises (Deferred objects using jQuery).


The Problem with Asynchronous Calls

Let’s take a simple example of retrieving and updating list items:

javascript

function updateListItems() {

    var clientContext = new SP.ClientContext.get_current();

    var oList = clientContext.get_web().get_lists().getByTitle('MyList');

     var camlQuery = new SP.CamlQuery();

    camlQuery.set_viewXml('<View><RowLimit>1</RowLimit></View>');

     this.collListItem = oList.getItems(camlQuery);

    clientContext.load(collListItem);

     clientContext.executeQueryAsync(

        function () {

            console.log("Execute second after retrieving the list items");

        },

        function (sender, args) {

            console.error('Request failed: ' + args.get_message());

        }

    );

     console.log("Execute first");

}

Output (Unexpected Order):

Execute first

Execute second after retrieving the list items

The above behavior is due to the asynchronous nature of executeQueryAsync(). Our log “Execute first” is running before the list items are retrieved.


Solution: Using jQuery Deferred / Promise

To handle such cases, we can wrap executeQueryAsync() inside a Deferred object and return a Promise. This way, we can wait until the SharePoint call is complete and then perform the next operation.


Synchronous Execution with Promise

Here's how we can convert the above logic to work in a pseudo-synchronous way using Promises:

javascript

function getListItemsWithPromise() {

    var deferred = $.Deferred();

    var clientContext = new SP.ClientContext.get_current();

    var oList = clientContext.get_web().get_lists().getByTitle('MyList');

     var camlQuery = new SP.CamlQuery();

    camlQuery.set_viewXml('<View><RowLimit>1</RowLimit></View>');

     var collListItem = oList.getItems(camlQuery);

    clientContext.load(collListItem);

     clientContext.executeQueryAsync(

        function () {

            console.log("Retrieved list items");

            deferred.resolve(collListItem);

        },

        function (sender, args) {

            console.error('Request failed: ' + args.get_message());

            deferred.reject(args.get_message());

        }

    );

     return deferred.promise();

}

Now, use the Promise like this:

javascript

function updateListWithPromise() {

    console.log("Execute first");

     getListItemsWithPromise()

        .then(function (items) {

            // Here, you can update list items

            var listItemEnumerator = items.getEnumerator();

            while (listItemEnumerator.moveNext()) {

                var oListItem = listItemEnumerator.get_current();

                oListItem.set_item('Title', 'Updated Title');

                oListItem.update();

            }

            var context = SP.ClientContext.get_current();

            context.executeQueryAsync(

                function () {

                    console.log("List item updated successfully.");

                },

                function (sender, args) {

                    console.error('Update failed: ' + args.get_message());

                }

            );

        })

        .catch(function (error) {

            console.error("Error: " + error);

        });

     console.log("This runs immediately after initiating the async call.");

}


Output (Expected Order):

Execute first

Retrieved list items

List item updated successfully.

This runs immediately after initiating the async call.


Conclusion

While SharePoint’s JavaScript Client Object Model is inherently asynchronous, wrapping your executeQueryAsync() calls in Promises (using jQuery Deferred) helps you write cleaner, more predictable code. It allows you to chain operations and control execution flow more effectively, which is crucial when working with dependent SharePoint actions.

This pattern is especially useful when you're:

  • Updating multiple items one after the other
  • Waiting for one operation to complete before starting another
  • Handling complex workflows in client-side SharePoint applications

Happy coding! 🚀