简体   繁体   中英

Why does jQuery.then() behave differently when using $.deferred and $.ajax

I've been trying to get my head around the jQuery Deferred object. My intention is to inspect each ajax response (success/fail). I want to do this without interfering with other code that declares a typical $.ajax().done().fail() request.

I've user the $.ajaxPrefilter() to get each ajax request before it is executed. Using the .then() method on the jqXHR object, I've managed to add a function that will be called before the .done() method placed on the original $.ajax() call

The code below will print out the following:

def done
def then
2nd ajax prefilter then
2nd ajax done
2nd ajax then
ajax done
ajax then

What I don't understand is why the prefilter step executes first. I would have expected it to have been executed last, or not at all.

The behaviour is what I want, but I don't understand why.

// this is a typical usage of deferred with two done functions added, the second via .then()
var def = $.Deferred();
def.done(function(){
    document.write("def done<br>");
});
def.then(function(){
    document.write("def then<br>");
});
def.resolve();

// this is a typical ajax request with a done function added, followed by another using .then()
$.ajax("/echo/json/").done(function(){
    document.write("ajax done<br>");
}).then(function(){
    document.write("ajax then<br>");
});

// for the third request i intercept and call the .then() method 
$.ajaxPrefilter( 
    function( options, originalOptions, jqXHR ) {
                jqXHR.then(function(data, textStatus, jqXHR){
                     document.write("2nd ajax prefilter then<br>");
                    });
            });

// create a typical ajax request. these will be executed after the prefilter .then()
$.ajax("/echo/json/").done(function(){
    document.write("2nd ajax done<br>");
}).then(function(){
    document.write("2nd ajax then<br>");
});

Thanks in advance for any help

UPDATE: ------------

From @Bergi response, the code below demonstrates how the $.ajaxPrefilter() is called before the done().

$.ajaxPrefilter( 
    function( options, originalOptions, jqXHR ) {
            document.write("prefilter function within $.ajax call<br>");
                jqXHR.then(function(data, textStatus, jqXHR){
                     document.write("2nd ajax prefilter then<br>");
                    });
            });

var functionToRunWhenDoneIsCalled = function() {
    document.write("done is called function<br>");
    return function(){
       document.write("2nd ajax done<br>");
    }
}

$.ajax("/echo/json/").done(
    (functionToRunWhenDoneIsCalled)()
).then(function(){
    document.write("2nd ajax then<br>");
});

This outputs:

prefilter function within $.ajax call
done is called function
2nd ajax prefilter then
2nd ajax done
2nd ajax then

Which answers my question about how the .then() method is attached to the deferred jqXHR object before the .done() method.

In your case, there is no difference between adding callbacks with .done() or with .then() . Using only .done() would be enough.

What I don't understand is why the prefilter step executes first. I would have expected it to have been executed last, or not at all.

Callbacks are executed in the order they are added to the deferred object. And the prefilter is executed inside of $.ajax , ie the callback is attached even before the $.ajax call returns and your done and then handlers can be attached.

All .then does if you don't return a deferred object is add another done fail and/or progress handler to the deferred object. with that in mind, it makes complete sense for the .then added in the pre-filter to execute before the one added after $.ajax() because the code in the pre-filter callback happened first. The callbacks get triggered first in first out.

What I don't understand is why the prefilter step executes first. I would have expected it to have been executed last, or not at all.

You've attached another "thing to do" to the jqXHR that is associated with the ajax request. Since it's a pre -filter, that gets attached before the standard done/fail that the ajax request uses. Handlers run in the order they were attached and the prefilter one is therefore first.

Note that since the prefilter only attached a single function in the .then() method, nothing will run if the request fails for some reason. Sounds like you'd want to have the second (failure handler) arg as well.

As for the completion order of the two different ajax requests, that is not predictable. It will just depend on which one returns first.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM