Lachezar Balev
Lachezar Balev

Reputation: 12041

Chaining unknown number of AJAX requests

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

Answers (2)

Roamer-1888
Roamer-1888

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

Ionică Bizău
Ionică Bizău

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

Related Questions