I have a simple array of urls, and I want to load each one with jQuery. I was using $.get
, but I cannot seem to get it to work with $.Deferred
, so I switched to $.ajax
- I almost have it working, but the results I am getting are .. odd. I was hoping someone could help me make this work better.
var results = [], files = [
'url1', 'url2', 'url3'
];
$.when(
$.ajax(files[0]).done(function(data) {
results.push(data); console.log("step 1.0");
}),
$.ajax(files[1]).done(function(data) {
results.push(data); console.log("step 1.1");
}),
$.ajax(files[2]).done(function(data) {
results.push(data); console.log("step 1.2");
})
).then(function(){
console.log("step 2");
});
This should output..
- step 1.0
- step 1.1
- step 1.2
- step 2
And then the results
array contains the result of all 3 ajax requests. Is this possible?
First off, you have to decide if you want your three ajax calls to be processed in parallel (running all at the same time, with less overall running time) or in sequence where one ajax calls runs, completes and then you launch the next ajax call. This is a key design decision that impacts how you do this.
When you use
$.when()
you are launching all three ajax calls in parallel. If you examine the results only when all have completed, you can still process the results in a specific order (since you will be processing them only when all results are available and they will be available in the order requested). But, when doing it this way all the ajax calls will be initially sent at once. This will give you a better end-to-end time so if this is feasible for the types of requests, this is generally a better way to do it.To do that, you can restructure what you have to something like this:
Run in Parallel
Because you're waiting until the
.done()
handler for$.when()
has been called, all the ajax results are ready at once and they are presented by$.when()
in the order they were requested (regardless of which one actually finished first), so you get the results as quick as possible and they are presented in a predictable order.Note, I also moved the definition of the
results
array into the$.when()
done handler because that's the only place you know the data is actually valid (for timing reasons).Run in Parallel - Iterate Arbitrary Length Array
If you had a longer array, you might find it better to iterate through your array with something like
.map()
to process them all in a loop rather than listing them individually:Sequence the Ajax Calls
If, on the other hand, you actually want to sequence your ajax calls so the 2nd one doesn't start until the first one finishes (something that may be required if the 2nd ajax call needs results from the 1st ajax call in order to know what to request or do), then you need a completely different design pattern and
$.when()
is not the way to go at all (it only does parallel requests). In that case, you probably just want to chain your results withx.then().then()
and you can then output the log statements in the sequence you asked for like this.Console Output:
This structure can also be put into a loop to automatically run it for N sequential ajax calls if your array of files is longer. While you could collect the results as you go into the
results
array, often the reason things are done sequentially is that the prior results are consumed by the next ajax call so you often only need the final result. If you wanted to collect the results as you go, you could certainly push them into theresults
array at each step.Notice, the advantages that promises offer here in that you can sequence operations while staying at the same top level of nesting and not getting further and further nested.
Sequence the Ajax Calls - Iterate Arbitrary Length Array
Here's what the sequencing would look like in a loop:
Console Output:
The
Array.prototype.reduce()
method works handily here because it accumulates a single value as you process each individual array element which is what you need to do as you add.then()
for each array element. The.reduce()
iteration is started with an empty/resolved promise with$().promise()
(there are other ways to also create such a promise) which just gives us something to start doing.then()
on that is already resolved.You should access the return values from .then instead of each .done. Additionally,
.map
is your friend.the arguments passed to .then will be in the same order that they were passed to .when