Reputation: 12041
I'm a bit confused despite the number of posts I've read...
I want to chain N
AJAX requests where N >= 1
. N
depends on the response of the first request and there is a dependency between the first request and the remaining requests. I do not want to freeze the browser.
Since the use-case is a bit complicated I'll try to simplify. Imagine that we have an API that may retrieve all users of an app and the individual address of each user. I want to display all of the addresses. Something like this:
function doSomething() {
var addresses = '';
$.getJSON('/api/users', function(data) {
$(data).each(function(id, user) {
$.getJSON('/api/user/' + user.id + '/address', function(address) {
addresses += address.displayName + '<br/>';
});
});
});
// do something with the addresses and display it somewhere
$("#xxx").append(addresses);
}
What is the best way to do that?
Upvotes: 2
Views: 1216
Reputation: 19288
You may find it more useful to refactor the code to provide a getAddresses()
function that does just that - gets addresses. The function would need to return a promise.
function getAddresses() {
return $.getJSON('/api/users').then(function(data) {
return $.when.appy(null, data.map(function(user) {
return $.getJSON('/api/user/' + user.id + '/address');
})).then(function () {
//convert the function's arguments to an array of addresses
return Array.prototype.slice.apply(arguments).map(function(a) {
return a[0];//crazy but that's the way jQuery's ajax functions deliver their data
});
});
});
}
Now, where getAddresses()
is called, that's where to do something with the addresses :
var promiseOfAddresses = getAddresses().then(function(addresses) {
// do something with the addresses and display it somewhere
var displayNames = addresses.map(function(address) {
return address.displayName;
});
$("#xxx").append(displayNames.join('<br/>'));
return addresses;
});
The promiseOfAddresses =
assignment is not strictly necessary to answer the question, but would be useful if you needed to do anything else with the addresses further on in your code.
promiseOfAddresses.then(function(addresses) {
//do something else with the addresses here
});
Upvotes: 0
Reputation: 113475
Use a complete
variable this way:
function doSomething() {
var addresses = '';
$.getJSON('/api/users', function(data) {
var complete = 0;
$(data).each(function(id, user) {
$.getJSON('/api/user/' + user.id + '/address', function(address) {
addresses += address.displayName + '<br/>';
if (++complete !== data.length) { return; }
// do something with the addresses and display it somewhere
$("#xxx").append(addresses);
});
});
});
}
Also, using promises is another solution:
function doSomething() {
var addresses = '';
$.getJSON('/api/users', function(data) {
var $allAjax = [];
$(data).each(function(id, user) {
$allAjax.push($.getJSON('/api/user/' + user.id + '/address', function(address) {
addresses += address.displayName + '<br/>';
}));
});
$.when.apply($, $allAjax).done(function () {
// do something with the addresses and display it somewhere
$("#xxx").append(addresses);
});
});
}
To keep the order of addresses, you should use an array:
var addresses = [];
Instead of += address.displayName + "<br/>"
you will do:
addresses[id] = address.displayName;
And at the end just override addresses
variable:
addresses = addresses.join("<br/>");
Upvotes: 3