Question

I'm trying to use a deferred promise in an AJAX loop because using async: false is locking up the page. I've poked around at other similar uses but I don't understand why $.when().apply().then() never executes.

    var userData = {};  
    userData.user1 = {"creds":{"userid":"user1@test.com","password":"user1pass","country":"us"}};
    userData.user2 = {"creds":{"userid":"user2@test.com","password":"user2pass","country":"mx"}};
    userData.user3 = {"creds":{"userid":"user3@test.com","password":"user3pass","country":"ca"}};
    function loginUser(credentials){
        var def = new $.Deferred();
        requests = [];
        $.each(credentials, function(k, v){
            var promise = $.ajax({
                url : '/path/to/my/uri', 
                type : 'post',
                contentType : 'application/json',
                data : JSON.stringify(v)
            });
            requests.push(promise); 
        });
        console.log(requests);
        // check if all ajax calls have finished
        $.when.apply($, requests).then(function() {
            // this never happens
            def.resolve();
            return def.promise();
            console.log('the request is done');
        });
    }
    loginUser(userData);


//updated snippet
var userData = {};  
userData.user1 = {"creds":{"userid":"user1@test.com","password":"user1pass","country":"us"}};
userData.user2 = {"creds":{"userid":"user2@test.com","password":"user2pass","country":"mx"}};
userData.user3 = {"creds":{"userid":"user3@test.com","password":"user3pass","country":"ca"}};
function loginUser(credentials){
    var requests = $.map(credentials, function(k, v){
    return $.ajax({
            url : '/path/to/my/uri', 
            type : 'post',
            contentType : 'application/json',
            data : JSON.stringify(k)
        });
    });
    console.log(requests);
    // return a promise that will resolve when all ajax calls are done
    return $.when.apply($, requests);
}

loginUser(userData).done(function() {
// all logins completed here
console.log('inside done');
}).fail(function() {
// will be called if any login failed
console.log('inside fail');
});

// current syncronous functionality
function loadUrls(credentials){
    $.each(credentials, function(k, v){
        $.ajax({
            url : '/path/to/my/uri', 
            type : 'post',
            contentType : 'application/json',
            success: function(data, textStatus, jqXHR) {
                $('body').append('<a target="_blank" href="'+data.userInfo+'">'+data.userName+' '+k+'</a><br />');
            },
            data : JSON.stringify(v),
            error : function(jqXHR, textStatus, errorThrown){
                return;                 
            },
            async : false      
        });
    });
}
Was it helpful?

Solution

Your log statement in here:

    $.when.apply($, requests).then(function() {
        // this never happens
        def.resolve();
        return def.promise();
        console.log('the request is done');
    });

will never happen because it's right after a return statement.

Perhaps you meant to return def.promise() from loginUser(). But, alas, there's really no reason to create this extra deferred at all. $.when() already returns a promise that can be used in this way.


Here's what I think you might actually want:

var userData = {};  
userData.user1 = {"creds":{"userid":"user1@test.com","password":"user1pass","country":"us"}};
userData.user2 = {"creds":{"userid":"user2@test.com","password":"user2pass","country":"mx"}};
userData.user3 = {"creds":{"userid":"user3@test.com","password":"user3pass","country":"ca"}};
function loginUser(credentials){
    var requests = [];
    $.each(credentials, function(k, v){
        var promise = $.ajax({
            url : '/path/to/my/uri', 
            type : 'post',
            contentType : 'application/json',
            data : JSON.stringify(v)
        });
        requests.push(promise); 
    });
    console.log(requests);
    // return a promise that will resolve when all ajax calls are done
    return $.when.apply($, requests);
}

loginUser(userData).done(function() {
   // all logins completed here
}).fail(function() {
   // will be called if any login failed
});

Or, as Benjamin Gruenbaum suggested, you can shorten the code a bit by using $.map() instead of $.each().

var userData = {};  
userData.user1 = {"creds":{"userid":"user1@test.com","password":"user1pass","country":"us"}};
userData.user2 = {"creds":{"userid":"user2@test.com","password":"user2pass","country":"mx"}};
userData.user3 = {"creds":{"userid":"user3@test.com","password":"user3pass","country":"ca"}};
function loginUser(credentials){
    var requests = $.map(credentials, function(k, v){
        return $.ajax({
            url : '/path/to/my/uri', 
            type : 'post',
            contentType : 'application/json',
            data : JSON.stringify(v)
        });
    });
    console.log(requests);
    // return a promise that will resolve when all ajax calls are done
    return $.when.apply($, requests);
}

loginUser(userData).done(function() {
   // all logins completed here
}).fail(function() {
   // will be called if any login failed
});

If what you were saying is that your $.when.done() handler never got called (even after you put the console.log() statement in a place that it will get executed), then the only way that happens is if one of your ajax calls does not complete successfully. You can watch for that by adding a .fail() handler in addition to a .done() handler.


If what you're really trying to do with your multiple logins is to just find out if at least one login that succeeds (even if the others fail), then you will need different logic for detecting that condition because $.when() will only tell you if all promises were resolved or if any promise was rejected. It won't tell you if at least one was accepted. To do that, we'd have to know what happens in the ajax call if the login is rejected. Does the ajax call still succeed, but the results show that the login failed? If so, then you'd have to examine the data passed to the .done() handler to see if at least one login succeeded.

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