Optimal/preferred way to call 'SP.ClientContext.executeQueryAsync' in SharePoint
Asked Answered
S

2

10

I have been learning client-side object model and came across the method executeQueryAsync. I found there are quite a few ways to call this method. Some of the one I found were these:

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

// Option 1
context.executeQueryAsync(
    function(sender, args){ },
    function(sender, args){ }
);

// Option 2
context.executeQueryAsync(
    Function.createDelegate(this, _onSucceed), 
    Function.createDelegate(this, _onFail)
);

// Option 3
context.executeQueryAsync(
    Function.createDelegate(this, this._onSucceed), 
    Function.createDelegate(this, this._onFail)
);

// Option 4
context.executeQueryAsync(_onSucceed, _onFail);

Which of this way is the most optimal/preferred one? Also what does the statement Function.createDelegate do? The documentation for this function seems to be very cryptic for me.

Schwitzer answered 21/7, 2014 at 16:31 Comment(0)
C
18

First I would say there is no 'optimal way' as these all just behave somewhat differently... Second, I would add this isn't so much a SharePoint or executeQueryAsync specific thing as it is a JS thing in general...

Next we need to understand that executeQueryAsync expects two functions as arguments: the first is a function to perform if executeQueryAsync succeeds, the second is a function to perform if the method encounters an error. These functions are passed parameters (from executeQueryAsync, not from your JS) representing the sending object as well as an arguments object that can have some data (args.get_message() and args.get_stackTrace() are common in the case of a failed call)

In your 'Option 1' example, executeQueryAsync is given two anonymous functions, you won't be able to re-use them anywhere, but if the behavior is simple this may be sufficient.

In Option 2 you use the createDelegate method to give the success and failure callbacks a context -- this speaks to scoping within JavaScript; if you need to reference a variable that is only accessible in the function that calls executeQueryAsync, you'll need to use this sort of pattern so that this within the callback references the function that called executeQueryAsync instead of the success or failure function that you're now in. You can think of creating a delegate as the calling function calling on some other function, but saying 'I want that function to be able to see what I can see no matter where it's located at within the code.' This may all seem a bit arcane, but such is scoping within JavaScript... You could completely circumvent the need for doing this by referencing variables at higher scope levels (say inside of a function that contains the calling method as well as the success and failure methods)

Option 3 is just like Option 2, except it just specifies that the _onSucceed or _onFail functions should be the ones that are contained within the calling object

Option4 is just like Option 1, except that you've named the functions (and that they are available within the current scope) and are calling them by name.

I usually use something like option 2, or option 4 -- but I hope you can see that it really just depends on how you're trying to structure your code.

EDIT: In response to a comment about Function.createDelagate() -- It seems to just be a helper in an ASP.NET script resource; it does nothing other than calling apply() (which is the standard JS way of doing this -- see MDN documentation here). It might also provide some backward compatibility somewhere within ASP.NET, but I'm not really sure!

Here is the code for the function from a script resource file in my SP environment:

Function.createDelegate = function(a, b) {
    return function() {
        return b.apply(a, arguments)
    }
};

And as a bonus, I was thinking about how I use executeQueryAsync and I realized that I actually use it more often like option 1, with a promise pattern using jQuery deferreds like this:

function getSPDataAsync(context) {
    var deferred = $.Deferred();
    context.executeQueryAsync(function(sender, args) {
        deferred.resolve(sender, args);
    }, function(sender, args) {
        deferred.reject(sender, args);
    });
    return deferred.promise();
}

Then you can do things a little less-spaghetti-like, such as:

...
ctx.load(items);
getSPDataAsync(ctx).then(function() {
    //do some stuff with the data when the promise resolves
});

Just in case anyone cares! :)

Crossopterygian answered 21/7, 2014 at 17:12 Comment(3)
Beautiful explanation John. Thanks! One more thing. The statement Function.createDelegate seems to be from SharePoint as I cannot find any official documentation for it. Am I correct?Schwitzer
Naveen, it seems to just be a helper from an ASP.NET script resource -- I've updated my answer to include some more information on it. Function.createDelegate is not in any native implementations of JS.Crossopterygian
jQuery Deferred really does help here. I do the exact same thing.Mansuetude
N
-3
Please try the below answer...this should help..Below code uses the context.ExecutequeryAsync method but since the items are captured separately on a string array there should not be any issues with respect to asynchronous behavior..

<style>
table { table-layout: fixed; }
td { width: 50%; }
</style><script type="text/javascript">
    function ShowselectedItems() {
        var ctx = new SP.ClientContext.get_current();
                web = ctx.get_web();
         if (ctx != undefined && ctx != null) {
            var listId = SP.ListOperation.Selection.getSelectedList();
                        var oList = ctx.get_web().get_lists().getByTitle('Testform'); // Put your list name here        
            var selectedItems = SP.ListOperation.Selection.getSelectedItems(ctx);
                        var camlquerystr = '';
                      if(selectedItems.length > 0){
                        if(selectedItems.length == 1)
                        {
                            camlquerystr += '<Where><Eq><FieldRef Name=\'ID\'/><Value Type=\'Number\'>' + selectedItems

[0].id + '</Value></Eq></Where>';
                        }
                        else if(selectedItems.length == 2)
                        {
                            camlquerystr += '<Where><Or><Eq><FieldRef Name=\'ID\'/><Value Type=\'Number\'>' + selectedItems

[0].id + '</Value></Eq><Eq><FieldRef Name=\'ID\'/><Value Type=\'Number\'>' + selectedItems[1].id + 

'</Value></Eq></Or></Where>';
                        }
                        else
                        {
                        var i;
                        camlquerystr += '<Where>';
                        for (i = 0; i < selectedItems.length - 1; i++) {
                               camlquerystr += '<Or><Eq><FieldRef Name=\'ID\'/><Value Type=\'Number\'>' + selectedItems

[i].id + '</Value></Eq>';
                        }
                        camlquerystr += '<Eq><FieldRef Name=\'ID\'/><Value Type=\'Number\'>' + selectedItems[i].id + 

'</Value></Eq>';
                        for (i = 0; i < selectedItems.length - 1; i++) {
                               camlquerystr += '</Or>';
                        }
                        camlquerystr += '</Where>';
                        }
                       }
                       else
                       {
                           alert('Please select your item');
                           retrun;
                       }
                        var camlQuery = new SP.CamlQuery();
                        camlQuery.set_viewXml('<View><Query>' + camlquerystr + '</Query></View>');
                        this.collListItem = oList.getItems(camlQuery);

                        ctx.load(collListItem, 'Include(Id, Title,Name,First_x0020_Name,Last_x0020_Name)');
                        ctx.executeQueryAsync(Function.createDelegate(this, this.success), Function.createDelegate(this, 

this.failed));
         }
       }

        function success() {
 var listItemInfo = '';
            var headstr = "<html><head><title></title></head><body>";
            var footstr = "</body>";
            var content;
    var listItemEnumerator = collListItem.getEnumerator();

    while (listItemEnumerator.moveNext()) {
        var oListItem = listItemEnumerator.get_current();
        content += "<table border='1' width='100%' style='table-layout: fixed;'><tr><td>Title:</td><td>" + oListItem.get_item('Title') + "</td></tr><tr><td>Name:</td><td>" + oListItem.get_item('Name') + "</td></tr><tr><td>First Name:</td><td>" + oListItem.get_item('First_x0020_Name') + "</td></tr><tr><td>LastName:</td><td>" + oListItem.get_item('Last_x0020_Name') + "</td></tr></table><br/><br/>";

    }

 w = window.open("", "_blank", "k");
                 w.document.write(headstr + content + footstr);
                 w.print();
        }

        function failed(sender, args) {
            alert('failed. Message:' + args.get_message());
        }        
</script><a href="#" onclick="javascript:ShowselectedItems();">Show Items</a>​​​​​
Numerator answered 9/3, 2018 at 8:8 Comment(1)
Please edit your answer adding some explanations. Avoid code only answer, take a look to : stackoverflow.com/help/how-to-answerKelbee

© 2022 - 2024 — McMap. All rights reserved.