Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery ajax deferred callback orders

Tags:

jquery

ajax

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.

like image 756
Justin Carlson Avatar asked May 27 '13 20:05

Justin Carlson


2 Answers

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.

like image 113
freakish Avatar answered Oct 19 '22 19:10

freakish


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;
}
like image 28
Tomas Kirda Avatar answered Oct 19 '22 20:10

Tomas Kirda