Cliff Chambers Cliff Chambers - 7 months ago 46
Javascript Question

jquery multiple deferred chaining

This is not working at all for me. Basically, if I step through it, it performs in the order where I have breakpoints (BREAKPOINT1, BREAKPOINT2, etc)

Due the way I am required to do sharepoint development at the moment, everything must be asynchronous. Loading data for one edit list item results in maybe 10-12 asynchronous calls. So I have chains several layers deep everywhere. This is the one that is completely breaking the functionality.

EDIT- What I want to happen. I want to call get children, get the group id's from a list, call getgroupname and get the group names for all id's (as many as 50) and then dump the array contents into a hidden field so when I update the list, it doesn't go and create a new record for each of these group ids as it would on a new list item. So when I click "update" it will grab the id's and create a new listitem for any group not present in the hidden field. This is a parent/child relationship, each group that is selected results in a new list item, but I only need one per group, not multiple.

I need to know how to fix this chain so that it returns my group names to the hidden field. I'm just having issues getting there and am hoping someone can spot my breakdown in logic.



function getchildren(id) {
var retval = [];
var url = "call to sharepoint 2013 url";
var ajaxPromise = restfulAPICall(url); //inside of this includes return $.ajax
ajaxPromise.then(function (data) {
$.each(data.d.results, function (i, v) {
var def = new $.Deferred();
getGroupname(v.Assigned_x0020_ToId.results[0]).done( //BREAKPOINT4 - multiple hits
function (val) {
def.resolve(val); //BREAKPOINT5
}
)
.done(function () {
reval.push(def); //BREAKPOINT6 (then back to 5, etc for # of results)
});
});
});
return $.when.apply(undefined, retval).promise(); //BREAKPOINT2
}

function getGroupName(v) {
var d = $.Deferred();
var ctx = new SP.ClientContext.get_current();
var groups = ctx.get_web().get_siteGroups();
var groupname = groups.getById(v);
ctx.load(groupname);
ctx.executeQueryAsync(function () {
d.resolve(groupname.get_title());
},
function (sender, err) {
d.reject(err);
});
return d.promise();
}

//somewhere else
if (!isChild) {
getChildren(id).then(function (val) { //BREAKPOINT1
$('#children').val(val); //BREAKPOINT3 - value blank
});
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="hidden" id="children" />




Answer

I suppose the heart of the matter is that, in getChildren, return $.when.apply(undefined, retval).promise(); executes immediately, before any of your async code has had time to do anything, and so retval remains an empty array and the returned promise immediately resolves to an empty array. The fix might look something like this, or at least this should point you in the right direction:

function getChildren() {
  var deferredResult = $.Deferred();
  var url = "call to sharepoint 2013 url";
  var ajaxPromise = restfulAPICall(url);
  ajaxPromise.then(function (data) {
    var retval = [];
    $.each(data.d.results, function (i, v) {
      retval.push(getGroupName(v.Assigned_x0020_ToId.results[0]));
    });
    // Only resolve promise returned from getChildren() once you have actually processed all the group names. Array.prototype.slice.call(arguments) converts arguments to real array
    $.when.apply(undefined, retval).then(function() {
      deferredResult.resolve(Array.prototype.slice.call(arguments));
    });
  });

  return deferredResult.promise();
}

Here is a fiddle, simplified from your originally provided code, with some mocked AJAX data to see if this might be what you are looking for...