Using jQuery deferred and promise inside loop

2019-08-25 11:57发布

问题:

This is what i am trying to achieve,

I have a sorted array which i pass to jQuery each. Inside each there is an ajax call which will fetch me the desired data and every time push into another array(lets call it allJsonData). Finally i display allJsonData. Problem is whenever i display allJsonData, elements are always displayed inconsistently (not alphabetically/random order). I am expecting allJsonData to be displayed alphabetically (that is AList data first, BList data second, CList data third and so on). I am new to jQuery deferred and promise. Thanks in advance.

var sortedArray = [AList, BList, CList, DList];
var promises = [];
var allJsonData = [];
$.each(sortedArray, function (index, value) {
var dfd = $.Deferred();
var url = _spPageContextInfo.webAbsoluteUrl  + ('/_api/Web/Lists/GetByTitle(' + "'" + value + "'" + ')/Items? + "SomeFilterParameters";

//AJAX CALL HERE//
.done(
  function (approvedListItems) {
    if (approvedListItems.d.results.length != 0) {
      $.each(approvedListItems.d.results, function (i, col) {
        allJsonData.push(col);//Push into master array
      });
    }//If closed
    dfd.resolve(allJsonData);
  }
);//Done closed
  promises.push(dfd);
});//jQuery Each closed
return $.when.apply($, promises).promise();

/****AJAX CALL****/

getListItems: function(url) {       
  var dfd = $.Deferred();
    $.ajax({
      url: url,
      type: "GET",
      headers: {
        "accept": "application/json;odata=verbose",
      },
    success: function (data) {
      dfd.resolve(data);
    },
    error: function (error) {
      dfd.reject(sender, args, "Error retrieving items");
    }
  });
  return dfd.promise();         
},

回答1:

I think you can do something as simple as this:

var sortedArray = [AList, BList, CList, DList];

Promise.all(sortedArray.map(function(value) {
    var url = ...;
    return getListItems(url);
})).then(function(results) {
    // results is an array of results from AList, BList, CList, DList in order
    let allJsonData = [];
    results.forEach(function(approvedListItems) {
        allJsonData.push.apply(allJsonData, approvedListItems.d.results);
    });
    // process allJsonData here
});

// simplify getListItems like this
getListItems: function(url) {       
    return $.ajax({
        url: url,
        type: "GET",
        headers: {
            "accept": "application/json;odata=verbose",
        }
    });
},

The general idea here is that you get a raw list (without processing the sub-results inside of it) for each of the items in sortedArray. Using either Promise.all() or $.when(), you will get the raw lists in order. Then, after you have all the raw lists, you can process them in order and build your allJsonData structure in order.

Also, you can remove the promise anti-pattern from getListItems(). $.ajax() already returns a promise so there is no need to wrap it in another promise.

You could convert this to use $.when() instead of Promise.all() if you really wanted to, but it is more complicated to use $.when() because of how it takes arguments and returns results.

Also, there's something wrong with the Javascript string syntax in your url variable. I don't know what you intended to do there so I'm not sure what to suggest, but you need to fix that too.