none
Wait Loop until the success callback handler finishes execution

    Question

  • I have the following scenario in CSOM. Here is the sample scenario. I want the loop ($resEles.each) to wait, until the success callback completes execution for every iteration of the loop. But unfortunately, the loop continues before the success callback completes execution for every iteration. I hope I am making it clear. Can anyone please explain, how to achieve this?

    $resEles.each(function () {
    var
    $ele = $(this);
    context.load(elements);

    context.executeQueryAsync(

    Function.createDelegate(this, this.onSucceeded),

    Function.createDelegate(this, this.onFailed));

    });

    function 

    }

    function

    onFailed(sender, args) {

    }

    onSucceeded(sender, args) { //do something here.
    Tuesday, October 08, 2013 3:44 PM

Answers

  • Below is some more example code. You need to keep the code that calls the taxonomy csom within its own function that returns a promise. This includes keeping the variables for the client context within the function's scope. Then you can use the jquery $.when.apply to execute the functions. The results for each call are in the arguments that are returned by default.

    function CallTest() {
      
        var tsIds = ["626db900-c668-45d0-b673-f43b6929f81f", "626db900-c668-45d0-b673-f43b6929f81f", "626db900-c668-45d0-b673-f43b6929f81f"];
        var functionCalls = [];
        
        functionCalls = $.map(tsIds, function (id) {
            var context = SP.ClientContext.get_current();
            var taxSession = SP.Taxonomy.TaxonomySession.getTaxonomySession(context);
            var termStores = taxSession.get_termStores();
            var termStore = taxSession.getDefaultSiteCollectionTermStore();
            var dfd = $.Deferred();
            termSet = termStore.getTermSet(id);
            terms = termSet.getAllTerms();
            context.load(terms);
            context.executeQueryAsync(
            function() {
                dfd.resolve(terms);
            },
            function(sender, args) {
                dfd.reject(args);
            });
            return dfd.promise();
        });
        
        $.when.apply($, functionCalls).done(function (results) {
            $.each(arguments, function (k, v) {
                alert(v.get_count().toString());
            })
        }).fail(function (e) {
            var i = e;
        });
    
       
    }


    Blog |SharePoint Field Notes Dev Tool | SPFastDeploy

    Wednesday, October 09, 2013 10:16 PM

All replies

  • You can use jquery promises to wait on all of your asynchronous requests to return. You can then perform code against the results in a logical order. You need to take your code and create a function that returns a jquery deferred (promise). Then use the jquery "when.done" function to wait on them.

    function doTest() {
    
        $.when(getSiteColumnsJSOM(), getSiteColumnsJSOM(), getSiteColumnsJSOM()).done(function (a1, a2, a3) {
            alert(a1);
            alert(a2);
            alert(a3);
        }).fail(function (f1, f2, f3) {
    
        });
    
    }
    
    function getSiteColumnsJSOM() {
        var dfd = $.Deferred();
        hostweburl = decodeURIComponent(getQueryStringParameter('SPHostUrl'));
        var ctx = new SP.ClientContext.get_current();
        hostWebContext = new SP.AppContextSite(ctx, hostweburl);
    
        currentWeb = hostWebContext.get_web();
        var fldList = currentWeb.get_fields();
        var stuff = ctx.loadQuery(fldList,'Include(Title)');
        ctx.executeQueryAsync(function (sender,data) {
            dfd.resolve(stuff.length);
        }, function (sender,args) {
            dfd.reject("error");
        }
    
        );
    
        return dfd;
    
    }

    http://api.jquery.com/category/deferred-object/

    http://www.shillier.com/archive/2013/03/04/using-promises-with-the-javascript-client-object-model-in-sharepoint-2013.aspx


    Blog |SharePoint Field Notes Dev Tool | SPFastDeploy

    Tuesday, October 08, 2013 8:34 PM
  • Thanks a lot for your response, Steve. I have a couple of quick questions though. Please excuse me, if these questions are silly.

    1. Where do I call the "doTest()" function from?

    2. Why have you called the "getSiteColumnsJSOM" method 3 times, in the $.when?

    Tuesday, October 08, 2013 9:13 PM
  • This is example code. I am showing you how to call an asynchronous function multiple times and wait for the results.

    Blog |SharePoint Field Notes Dev Tool | SPFastDeploy

    Tuesday, October 08, 2013 9:21 PM
  • I got that, Steve.

    What I am asking is that, where exactly do I need to call the "doTest()" function. Should I call it with every iteration within the loop or outside of the loop?

    Also, in your sample, you are calling it 3 times, but, in my case, what will be the construct?

    Also, what if the method called within the $.when has args? Should I pass those also, with every call or it's just the method name followed by paranthesis?

    Please excuse me for pestering you again and again with silly questions. But I am a little confused, since I saw some articles, which use a whole bunch of things like promise, when, then and other constructs. I am not sure, where exactly to put these constructs and how to process the results from the async call. Please advise.

    Tuesday, October 08, 2013 9:43 PM
  • The code you originally posted could be put into a function that would use the deferred promise. Then you could write a function that calls this new function. This function would use code I posted in the doTest function. So if you needed to call your function three times with arguments you could do

    .when(yourFunction(args), yourFunction(args), yourFunction(args)).done(results1, results2, results3)..


    Blog |SharePoint Field Notes Dev Tool | SPFastDeploy

    Tuesday, October 08, 2013 10:21 PM
  • Hi Steve,

    Since this call will be made inside a $.each, I won't be able to know the count upfront. But somehow, the deferred pattern seems to be working, but when I try to access the objects properties in the done method, I am getting an exception saying " terms.getEnumerator() The collection has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested." This error should not have occurred, if the promise had been resolved properly. I followed the same approach provided by you. Can you please let me know, what is the issue here?

    Wednesday, October 09, 2013 9:28 PM
  • Below is some more example code. You need to keep the code that calls the taxonomy csom within its own function that returns a promise. This includes keeping the variables for the client context within the function's scope. Then you can use the jquery $.when.apply to execute the functions. The results for each call are in the arguments that are returned by default.

    function CallTest() {
      
        var tsIds = ["626db900-c668-45d0-b673-f43b6929f81f", "626db900-c668-45d0-b673-f43b6929f81f", "626db900-c668-45d0-b673-f43b6929f81f"];
        var functionCalls = [];
        
        functionCalls = $.map(tsIds, function (id) {
            var context = SP.ClientContext.get_current();
            var taxSession = SP.Taxonomy.TaxonomySession.getTaxonomySession(context);
            var termStores = taxSession.get_termStores();
            var termStore = taxSession.getDefaultSiteCollectionTermStore();
            var dfd = $.Deferred();
            termSet = termStore.getTermSet(id);
            terms = termSet.getAllTerms();
            context.load(terms);
            context.executeQueryAsync(
            function() {
                dfd.resolve(terms);
            },
            function(sender, args) {
                dfd.reject(args);
            });
            return dfd.promise();
        });
        
        $.when.apply($, functionCalls).done(function (results) {
            $.each(arguments, function (k, v) {
                alert(v.get_count().toString());
            })
        }).fail(function (e) {
            var i = e;
        });
    
       
    }


    Blog |SharePoint Field Notes Dev Tool | SPFastDeploy

    Wednesday, October 09, 2013 10:16 PM
  • Thanks a lot, Steve. I tried with your code. Now it seems to be working. But, here I am seeing quite a few changes from your previous code. You seemed to have used an array called functionCalls, a jQuery map function, $.when.apply. These are different from the previous implementation. Also, now, I may need to use recursion to get the inner child elements. So for that, within the success method of $.when.apply's foreach loop, i should again have a similar implementation, correct?
    Wednesday, October 09, 2013 10:46 PM
  • Not sure what you are trying to accomplish, but I don't recommend recursion in JavaScript. The example is just showing you how to call a function multiple times with different arguments and wait until all are done executing. If you need to call another function after these have completed then the same construct should work.

    Blog |SharePoint Field Notes Dev Tool | SPFastDeploy

    Thursday, October 10, 2013 3:51 AM
  • Thanks a lot for the response, Steve. I will try to avoid recursion, but I don't think I can, if I have to build the whole tree. Do you know any alternatives?. Can we do something like below to avoid recursion?

    context.load(terms, 'Include('Parent', 'ChildTerms')');

     With that said, I had one more quick question. Earlier, I was using the Promise pattern within $.each construct. But now, the approach you have given me (of course, it's working and thanks a lot for that) doesn't use $.each. I am finding it a bit difficult in understanding that logic.  Can you please help me? Also, what is the difference between, $.when and $.when.apply?

    • Edited by Ven123 Thursday, October 10, 2013 5:25 PM
    Thursday, October 10, 2013 12:45 PM
  • I use the $map to return an array of promises. Then you can pass the array of promises to the $.when.apply function which will execute each promise in sequential order and then execute the code when all promises have completed. The $.each you were using was executing each function but with no waiting.

    Blog |SharePoint Field Notes Dev Tool | SPFastDeploy

    Thursday, October 10, 2013 9:19 PM
  • Great. Thanks a lot for the clear explanation, Steve. But, it's a bit disappointing to note from MS side that, neither they have proper REST support for Taxonomy nor there is a simple way in CSOM to get all the terms in one call. Moreover, somethings look really wierd in CSOM. For example, after getting a term, I am able to access some of it's properties without subsequent calls, but for some other properties, I am forced to make subsequent calls using the ExecuteQueryAsync method. After fetching a term, I am able to get it's ID, Name, ChildTermCount, isRoot without making further calls, but not able to get the parent of a term, without making another call. This is very confusing. I am not able to understand the pattern here.
    Friday, October 11, 2013 2:42 PM