0

I have a little code segment like this:

var requestArray = [];
thingArray.forEach(function (thing) {
    var uri = "https://server/_api/endpoint/things(" + thing.Id + ")";
    requestArray.push($.ajax({
        url: uri,
        method: 'GET',
        headers: {
            accept: 'application/json;odata=verbose'
        }
    }));
});
$.when(requestArray).done(function(responseArray) {
    responseArray.forEach(function(response) {
        // response.responseJSON is undefined
    });
});

and I realized I'm having the timing issue and responseJSON is undefined because I'm passing an array of Deferreds to $.when(), expecting it to be like Promise.all() (which does take an array), but $.when() just takes an unstructured "group" of Deferreds, so in my case $.when().done() is resolving instantly because of the Array object passed in.

I tried to hack my way around it by doing

$.when(requestArray.forEach(function(req){ return req; })).done(function(responseArray) { })

(can't destructure like ...requestArray because IE 11), but in that case responseArray ends up being undefined.

Looking back over the documentation for $.when(), I see that all the examples have a known number of requests, and therefore can set up the done function with a known number of parameters, like

$.when(req1, req2).done(function(resp1, resp2) { // etc })

So, how can I set up $.when().done() to work if I have an unknown number of requests? My thingArray in the example above is the result of a previous query, and could have any number of elements.

Dylan Cristy
  • 916
  • 11
  • 29

1 Answers1

0

You can use Function.apply() to invoke a function with an array as a list of parameters. To get the responses, iterate the arguments array like object:

$.when.apply($, requestArray).done(function() {
    for(var i = 0; i < arguments.length; i++) {
        console.log(arguments[i]);
    }
});

Demo:

var d1 = $.Deferred();
var d2 = $.Deferred();
var promiseArray = [d1.promise(), d2.promise()];

$.when.apply($, promiseArray).done(function() {
  for(var i = 0; i < arguments.length; i++) {
    console.log(arguments[i]);
  }
});

d1.resolve("Fish");
d2.resolve("Pizza");
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • But again, you are setting up the example with a known number of requests, so then you set up your `done` function with a _known_ number of parameters. I will not know the number of parameters to define in the `done` function because I don't know the number of responses I'm expecting. – Dylan Cristy Jan 25 '20 at 19:49
  • The `.done()` problem can be easily solved with `arguments`. See updated answer. – Ori Drori Jan 25 '20 at 19:53
  • That's starting to get there, but still a bit problematic, because if there is one request (and one response), there will be three arguments, `data`, `textStatus` and `jqXHR`. But, if there are more than one request (and more than one response), each argument will be an array with the array containing the `[data, textStatus, jqXHR]`. Is there any way to get more consistent behavior? Checking every time to see if the first argument is an object or an array and then having branching logic from there seems inefficient. – Dylan Cristy Jan 25 '20 at 20:37
  • You'll get an array of arrays back. Have you tested it? – Ori Drori Jan 25 '20 at 20:41
  • I have, and with one request I don't get a single array with the three things (making the argument count one), I get the `data`, `textStatus` and `jqXHR` as separate arguments, not grouped in an array, making the argument count three. For multiple requests/responses I get the array of arrays. – Dylan Cristy Jan 25 '20 at 20:53
  • This means that you'll need to check the length of the `requestArray` in the `.done()` block. If the length is 1, all arguments are part of a single request. – Ori Drori Jan 25 '20 at 22:30