6

I cannot get the done/fail/always callbacks on each of my ajax requests to execute before the deferred object's callbacks.

The problem I have is that some of my ajax requests may fail, but I need to execute the same code whether one fails or none fail.

Hard to explain exactly, so I made this fiddle to help show the problem I am having. http://jsfiddle.net/zZsxV/

var a1 = $.Deferred();
var a2 = $.Deferred();
var a3 = $.Deferred();

a1.done(function() {
    $('body').append('a1 done<br />');     
}).fail(function() {
    $('body').append('a1 fail<br />');     
}).always(function() {
    $('body').append('a1 always<br />');     
});

a2.done(function() {
    $('body').append('a2 done<br />');     
}).fail(function() {
    $('body').append('a2 fail<br />');     
}).always(function() {
    $('body').append('a2 always<br />');     
});

a3.done(function() {
    $('body').append('a3 done<br />');     
}).fail(function() {
    $('body').append('a3 fail<br />');     
}).always(function() {
    $('body').append('a3 always<br />');     
});

var def = $.when(a1, a2, a3);
def.always(function() {
    $('body').append('defer always <-- should be after all<br />'); 
});

setTimeout(function() {
    a1.resolve();
}, 5000);
setTimeout(function() {
    a2.reject();
}, 1000);
setTimeout(function() {
    a3.resolve();
}, 3000);

I read a lot of the answers on this topic but I don't believe any fit my need exactly.

Any more information needed to help please let me know and I'll add it once I get back. Thanks in advance.


Edit

I do understand what is happening. I just know how to do it to avoid this problem. I tried using .then as well with the same results. Once one request is rejected, it fires the fail callback before waiting for the other callbacks.

Justin Carlson
  • 2,670
  • 2
  • 18
  • 19
  • in case you didn't have already read this thread: http://stackoverflow.com/questions/5518181/jquery-deferreds-when-and-the-fail-callback-arguments – A. Wolff May 27 '13 at 21:11

3 Answers3

2

According to jQuery documentation $.when fires callbacks when either all of parameters are resolved or one of them is rejected. So how about using something like that instead of $.when:

var custom_when = function() {
    var args = $.makeArray(arguments);
    var callback = args.pop();
    var all = [];
    $.each(args, function(index, def) {
        def.always(function() {
            var idx = all.indexOf(def);
            if (idx !== -1) {
                all.splice(idx, 1);
            }
            if (!all.length) {
                callback();
            }
        });
        all.push(def);
    });
};

and you can use it like that:

custom_when(a1, a2, a3, function() {
    // do some stuff.
});

and jsFiddle demo.

freakish
  • 54,167
  • 9
  • 132
  • 169
  • 1
    This looks good. Thanks for the fiddle demo as well. I can't implement it into my build until tonight or tomorrow. But Once I do, I'll report back. – Justin Carlson May 28 '13 at 18:14
1

I see what you are trying to do here, but there is no convenient method. This works:

JSFIDDLE

$.when(a1).done(a2).done(a3).then(function() {
    $('body').append('defer always <-- should be after all<br />'); 
});

Other option would be to write your own simple function. See updated FIDDLE:

whenAllResolved(a1, a2, a3).done(function () {
    $('body').append('defer always <-- should be after all<br />');
});


function whenAllResolved(){
    var d = $.Deferred(),
        args = arguments,
        verifyDeferreds = function(){
            var allResolved = true;
            $.each(args, function(i, arg){
                if(arg.state() === 'pending'){
                    allResolved = false;
                    return false;
                }
            });
            if (allResolved){
                d.resolve();
            }
        };

    $.each(arguments, function(i, arg){
        arg.always(verifyDeferreds);
    });

    verifyDeferreds();

    return d;
}
Tomas Kirda
  • 8,347
  • 3
  • 31
  • 23
0

Hey i had a similar problem to this, i don't have the code with me but i will try to find it.

Anyway what i did was have a variable with the number of objects i needed to wait, and just decrease it when one of them finished, and then check if it was 0 if it is i could call the other function.

Luis Tellez
  • 2,785
  • 1
  • 20
  • 28
  • 1
    I actually had that same idea. I really wanted to avoid it, just for cleaner code purposes I suppose. Thanks for your feedback. I would be happy in seeing your solution just in case nobody else has an idea if this is possible without a workaround. – Justin Carlson May 27 '13 at 21:06
  • was the only way i found by the time that i needed it. – Luis Tellez May 27 '13 at 21:34