59

I have a problem with multiple ajax functions where the beforeSend of the second ajax post is executed before the complete function of the first ajax.

The loading class I am adding to the placeholder before sending is working for the first ajax call. However soon after the first ajax request completes the class is removed and never appends again on the second and further calls (remember recursive calls).

While debugging it shows that the beforeSend function of the second ajax call is called first and the complete function of the first ajax call is called later. Which is obvious, because the return data inserted in the page from the first ajax call starts the second call.

In short it's mixed up. Is there any way this can be sorted out?

The function code is as follows

function AjaxSendForm(url, placeholder, form, append) {
var data = $(form).serialize();
append = (append === undefined ? false : true); // whatever, it will evaluate to true or false only
$.ajax({
    type: 'POST',
    url: url,
    data: data,
    beforeSend: function() {
        // setting a timeout
        $(placeholder).addClass('loading');
    },
    success: function(data) {
        if (append) {
            $(placeholder).append(data);
        } else {
            $(placeholder).html(data);
        }
    },
    error: function(xhr) { // if error occured
        alert("Error occured.please try again");
        $(placeholder).append(xhr.statusText + xhr.responseText);
        $(placeholder).removeClass('loading');
    },
    complete: function() {
        $(placeholder).removeClass('loading');
    },
    dataType: 'html'
});
}

And the data contains the following snippet of javascript/jquery which checks and starts another ajax request.

<script type="text/javascript">//<!--
 $(document).ready(function() {
    $('#restart').val(-1)
    $('#ajaxSubmit').click();
});
//--></script>
Rehan Anis
  • 788
  • 1
  • 5
  • 7

2 Answers2

101

Maybe you can try the following :

var i = 0;
function AjaxSendForm(url, placeholder, form, append) {
var data = $(form).serialize();
append = (append === undefined ? false : true); // whatever, it will evaluate to true or false only
$.ajax({
    type: 'POST',
    url: url,
    data: data,
    beforeSend: function() {
        // setting a timeout
        $(placeholder).addClass('loading');
        i++;
    },
    success: function(data) {
        if (append) {
            $(placeholder).append(data);
        } else {
            $(placeholder).html(data);
        }
    },
    error: function(xhr) { // if error occured
        alert("Error occured.please try again");
        $(placeholder).append(xhr.statusText + xhr.responseText);
        $(placeholder).removeClass('loading');
    },
    complete: function() {
        i--;
        if (i <= 0) {
            $(placeholder).removeClass('loading');
        }
    },
    dataType: 'html'
});
}

This way, if the beforeSend statement is called before the complete statement i will be greater than 0 so it will not remove the class. Then only the last call will be able to remove it.

I cannot test it, let me know if it works or not.

RickL
  • 3,318
  • 10
  • 38
  • 39
Serge K.
  • 5,303
  • 1
  • 20
  • 27
  • I tried your solution and it gave me surprising results. First of all I used the var requestNumber = 0 and ++ it in beforeSend and checked it in complete/always. It shows the requestNumber == 2 and -- results in 1 which returns false in the check and loading class isn't removed. However it is never removed because I don't know why, it is generating one additional request for the whole sequence. Now the culprit is found, I think it's easy to resolve or post a bug request. – Rehan Anis Sep 06 '14 at 07:23
  • If the function fails, then it goes to error as well as complete. What if I only want it at complete, can I `return 1` at the end of complete so it doesn't go to error? – Robot Boy Jun 24 '15 at 06:12
11

It's actually much easier with jQuery's promise API:

$.ajax(
            type: "GET",
            url: requestURL,
        ).then((success) =>
            console.dir(success)
        ).failure((failureResponse) =>
            console.dir(failureResponse)
        )

Alternatively, you can pass in of bind functions to each result callback; the order of parameters is: (success, failure). So long as you specify a function with at least 1 parameter, you get access to the response. So, for example, if you wanted to check the response text, you could simply do:

$.ajax(
            type: "GET",
            url: @get("url") + "logout",
            beforeSend: (xhr) -> xhr.setRequestHeader("token", currentToken)
        ).failure((response) -> console.log "Request was unauthorized" if response.status is 401
user1429980
  • 6,872
  • 2
  • 43
  • 53
  • Thanks for the update, I updated my code and replaced success, error and complete callback functions with the newly defined, promised functions fail, done and always. However they did not resolves the problem because the problem was something different as I applied the solution provided by @Nathan P – Rehan Anis Sep 06 '14 at 07:19