0

I have a function that consumes data with a WCF service (in SharePoint). The service does not return a specific field that I need for items so I use the SharePoint Client Object Model to query for the field by using the ID I have in the returned result from the WCF service.

function LoadAllNews() {
var listUrl = "/_vti_bin/ListData.svc/Pages";

$.getJSON(listUrl,
 function (data) {
     $.each(data.d,
      function (i, result) {
          GetImageUrl(result.Id, function (image) {
              $(ConstructHtml(image, result.Title, result.Path, result.Name)).appendTo("#News");
          });
      });
 });

}

When I debug result here I always get the items returned in the same order but since the GetImageUrl executes a query async the items are not appended in the same order. Most of the times they do must some times it appears to be random since time to get the image varies:

function GetImageUrl(id, callback) {
    var context = new SP.ClientContext();
    var items = context.get_web().get_lists().getByTitle('Pages').getItemById(id);
    context.load(items);

    context.executeQueryAsync(function () {
        callback(items.get_item('PublishingRollupImage'));
    });
}

function ConstructHtml(imageUrl, title, path, name) {
    var html = "" // a long html string..
    return html;
}

I could post this on sharepoint.stackexchange but the audience is wider here and it's more of a question how to handle this with JavaScript than with SharePoint itself.

Any ideas on how I should approach this? I was thinking something like skip the image in LoadAllNews() and then when all items are appended use JavaScript/jQuery to load the image for each news item.

Thanks in advance.

John Doe
  • 188
  • 1
  • 14

2 Answers2

0

If the order of events matters, make it a synchronous procedure

JeremyWeir
  • 24,118
  • 10
  • 92
  • 107
0

Based on the fork function from my answer to this question: Coordinating parallel execution in node.js. I would do it like this:

var getImages = [];
var meta = [];
$.each(data.d,
  function (i, result) {
    getImages.push(function(callback){
      GetImageUrl(result.Id, callback);
    });
    meta.push({
      title : result.Title,
      path : result.Path,
      name : result.Name
    });
});

fork(getImages,function(images) {
  $.each(images,function(i,image){
      $(ConstructHtml(
          image,
          meta[i].title,
          meta[i].path,
          meta[i].name
      )).appendTo("#News");
   });
});

The implementation of fork is simply this:

function fork (async_calls, shared_callback) {
  var counter = async_calls.length;
  var all_results = [];
  function makeCallback (index) {
    return function () {
      counter --;
      var results = [];
      // we use the arguments object here because some callbacks 
      // in Node pass in multiple arguments as result.
      for (var i=0;i<arguments.length;i++) {
        results.push(arguments[i]);
      }
      all_results[index] = results;
      if (counter == 0) {
        shared_callback(all_results);
      }
    }
  }

  for (var i=0;i<async_calls.length;i++) {
    async_calls[i](makeCallback(i));
  }
}

The fork function above gathers asynchronous results in order so it does exactly what you want.

Community
  • 1
  • 1
slebetman
  • 109,858
  • 19
  • 140
  • 171