0

I have 2 nested AJAX requests in my code. They are fired when a button is pressed. I'm using Bootstrap's loading stages to change the button's text to "Loading…" while the AJAX requests are being processed.

The issue is that the button gets reset before the inner AJAX request has completed.

My expectation is that the complete callback of the outer AJAX request would be executed only after all the code in its success callback has been executed, but that's not what's happening.

The code's simplified version looks like this:

$('#button').on('click', function() {
$.ajax({
    url: …,
    type: 'post',
    data: …,
    dataType: 'json',
    beforeSend: function() {
        $('#button').button('loading');
    },
    complete: function() {
        $('#button').button('reset');
    },
    success: function(json) {
        if (json['error']) {
        …
        } else {
            $.ajax({
                url: …,
                dataType: 'json',
                success: function(json) {
                    for (var delayer = 1; delayer < 2000000000; delayer++) {}
                    …
                },
                error: …
            });
        }
    },
    error: …
});             
});

I've created the arbitrary delaying loop to be able to better test the effect on my machine. It gives me about 2 seconds of delay. The button resets almost instantly though.

Is that how it's supposed to work and if it's not – how can I fix it?

Azanyr
  • 197
  • 1
  • 9

1 Answers1

1

the complete function in your first AJAX request is being fired when your first AJAX request is completed. If you want it to be fired after the second one is completed, just move it into the second AJAX request. :)

$('#button').on('click', function() {
$.ajax({
    url: …,
    type: 'post',
    data: …,
    dataType: 'json',
    beforeSend: function() {
        $('#button').button('loading');
    },
    success: function(json) {
        if (json['error']) {
        …
        } else {
            $.ajax({
                url: …,
                dataType: 'json',
                complete: function() {
                    $('#button').button('reset');
                },
                success: function(json) {
                    for (var delayer = 1; delayer < 2000000000; delayer++) {}
                    …
                },
                error: …
            });
        }
    },
    error: …
});             
});

The reason you are confused is because the complete callback is fired at the same time the success callback is fired (approximately). The only difference is complete is fired no matter what the result, while success is only fired when it gets a positive response (i.e. HTTP 200 OK)

complete IS NOT fired when success is complete. For more info check out the jQuery documentation

The documentation does say that the complete is fired after success execution, but this is does not mean that all of success finishes before complete is even called. This has to do with asynchronous coding in JavaScript.

robere2
  • 1,689
  • 2
  • 16
  • 26
  • Thank you for the quick response! I've already tried this solution and you're right, but it doesn't work in my case, because I also need to reset the button if the PHP handler for the first AJAX request has send an error in the JSON array. – Azanyr Sep 02 '17 at 16:59
  • 1
    You'll need to modify your code, then. Add `$('#button').button('reset');` to the first half of your `if(json['error'])` statement as well and hopefully that will solve your problem. It will then reset the button if there's an error, or if there's no error then it will reset the button after the second AJAX request. – robere2 Sep 02 '17 at 17:02
  • I've tried to set a small timeout on the reset function and if it has ~50 miliseconds timeout, for some reason the button doesn't get reset after 50 ms, but gets reset after the success function has completed, which is what I want, but isn't there a better solution? – Azanyr Sep 02 '17 at 17:03
  • In general, timeouts are a bad idea in JavaScript. Most of the time you'll have a callback in asynchronous code, and a lot of people make the mistake of adding things such as timers to get around it. It can be a difficult thing to wrap your head around. Maybe reading the question at https://stackoverflow.com/questions/4559032/easy-to-understand-definition-of-asynchronous-event can help explain some. – robere2 Sep 02 '17 at 17:06
  • I think I got the basic idea of it now, thank you for the explanation! So it seems that the best thing to do is to repeat the `$('#button').button('reset');` function. – Azanyr Sep 02 '17 at 17:16
  • Exactly the part of the jQuery documentation that you mentioned, in which it is said that the `complete` callback is fired after the `success` and `error` callbacks are executed, had me confused. – Azanyr Sep 02 '17 at 17:23
  • Yep, that's what you should do. It is confusing. I edited my post when I noticed it to clarify it. – robere2 Sep 02 '17 at 17:57