Question

I'm currently trying to get around the async behavior of AJAX. Problem is, that I have an unspecified amount of AJAX calls that I all have to wait for. I'm creating with jQuery a deferred object that gets resolved manually as soon as the last ajax call has finished including its success-handler. works fine, but: it seems that the function, where all that happens, has terminated (and cleaned up all variables that were declared inside that function) before the then() function executes. I can only solve this problem by declaring the needed variable users globally.

If I declare

$().click(function() {
     /* inside here */ 
     var users = [];
});

then it doesn't work. Console states that the var users is not declared. (See code example).

What is a clean approach to solve this problem? Declaring all needed variables globally seems not really nice to me.

Link to jsfiddle with my code example

Was it helpful?

Solution

You will need to declare the variable in a scope where all functions can access it. As your getGroupMembers function is in the global scope, so your users variable needs to be. Move it into the ready or click scope, and you can declare the variable as local.

However, it would be much easier to pass the result of your request as arguments to resolve:

$(document).ready(function() {
    $('#message_send').click(function() {
        var deferred = getGroupMembers({
            page: 1,
            per_page: 100
        });
        deferred.then(function(users) {
            console.log(users);
        });
    });
});

function getGroupMembers(querydata) {
    var users = [];
    var deferredObject = new $.Deferred();
    …
    // some asynchronous tasks, callbacking either
        deferredObject.resolve(users);
    // or
        deferredObject.reject();
    …
    return deferredObject.promise();
}

For some syntactic sugar, you might as well just use the pipe method of the Ajax Deferred.


Recursive piped method:

function getGroupMembers(querydata) {
    return $.ajax({
        url: "/echo/json/",
        dataType: "json",
        data: querydata,
        cache: false
    }).pipe(function(data) {
        var user = data.response.UserActions,
            users = [];
        for (var u = 0; u < user.length; u++) {
            users.push(user[u].user.id);
        }
        if (querydata.page < data.meta.total_pages) {
            querydata.page++;
            return getGroupMembers(querydata).pipe(function(nextusers) {
                return users.concat(nextusers);
            });
        } else {
            return users;
        }
    });
}

OTHER TIPS

You can get around this with closures but I would suggest looking at async.js

If you want to pass the last response of an ajax call to the next function

https://github.com/caolan/async#series

Probably a better choice if you are running one ajax after an another is waterfall

https://github.com/caolan/async#waterfall

waterfall is the same as series but stops if any of the functions fail / series doesn't

In the event of running multiple ajax calls and just waiting until they are all finished

https://github.com/caolan/async#parallel

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top