Ben Kao
Ben Kao

Reputation: 661

First AJAX always happens before last nested AJAX done

I'm using nested AJAX. I disable button before all AJAX calls then enable button within .always in outermost AJAX. But the problem is that I think code to enable button happens before code within .done of innermost AJAX. Is it because the .always in outermost AJAX runs in parallel to .done in innermost AJAX? And one gets done before the other?

What's the workaround to this? Do I need to use promises so that the button enabling happens after all AJAX calls have been completed? If so, can you show me how? It seems advanced and I don't understand the code I've been reading around.

    function loginAndEnter() {
        $("#login-and-enter-btn").prop('disabled', true);
        $("#login-and-enter-btn").text('請稍等...');

        $.ajax({    //Outermost AJAX
            type:"GET",
            url:"/qrcode/login/",
            data:{
                "cellphone":document.getElementById("cellphone").value,
                "password":document.getElementById("password").value
            }
        })
        .done(function(responsedata) {
            var parsedJson = $.parseJSON(responsedata);

            if(parsedJson.result==1){
                document.getElementById("token").value = parsedJson.token;

                $.ajax({
                    type:"GET",
                    url:"/qrcode/entry/",
                    data:{
                        "token":document.getElementById("token").value,
                        "parking_lot_id":{{ $parking_lot_id }},
                        "in_or_out":{{ $in_or_out }}
                    }
                })
                .done(function(responsedata) {
                    var parsedJson = $.parseJSON(responsedata);

                    if(parsedJson.result==1){
                        $.ajax({
                            type:"GET",
                            url:"/qrcode/zero/",
                            data:{
                                "booking_id":parsedJson.Booking_ID[0].id,
                                "token":document.getElementById("token").value
                            }
                        })
                        .done(function(responsedata) {    //Innermost done
                            var parsedJson = $.parseJSON(responsedata);

                            if(parsedJson.result==1){
                                alert("進場成功! 請使用易停網APP繳費與出場.");
                                window.location.href = "/download";
                            }
                            else{
                                alert(parsedJson.title+"\n"+parsedJson.description);
                            }
                        })
                        .fail(function(xhr, status, errorThrown) {
                            ...
                        });
                    }
                    else{
                        alert(parsedJson.title+"\n"+parsedJson.description);
                    }
                })
                .fail(function(xhr, status, errorThrown) {
                    ...
                });
            }
            else{
                alert(parsedJson.title+"\n"+parsedJson.description);
            }
        })
        .fail(function(xhr, status, errorThrown) {
            ...
        })
        .always(function() {    //Outermost always
            $("#login-and-enter-btn").prop('disabled', false);
            $("#login-and-enter-btn").text('登入和升起柵欄進場');
        });
    }

Upvotes: 0

Views: 120

Answers (1)

Christoph
Christoph

Reputation: 281

The .always function doesn't wait for the other AJAX requests to be completed because it's invoked right after the outermost request gets a response. The fact that requests are nested, means that subsequent AJAX requests will be invoked after other ones are resolved, but if you'd like to do something only when and after all of them are resolved, Promises will be required.

I modified your code to show one way of achieving your goal with Promises and async/await function.

function firstAJAX() {
  return new Promise((resolve, reject) => {
    $.ajax({    //Outermost AJAX
      type:"GET",
      url:"/qrcode/login/",
      data:{
          "cellphone": 111111111111,
          "password": "pwd"
      }
    })
    .done(function(responsedata) {
      // var parsedJson = $.parseJSON(responsedata);
      var parsedJson = {};
      parsedJson.result = 1;

      if(parsedJson.result==1){
        resolve(responsedata);
      }
      else{
        alert(parsedJson.title+"\n"+parsedJson.description);
      }
    })
    .fail(function(xhr, status, errorThrown) {
      console.log(status);
    });

  });
}

function secondAJAX(data) {
  return new Promise((resolve, reject) => {
    $.ajax({
        type:"GET",
        url:"/qrcode/entry/",
        data:{
            "token": "token",
            "parking_lot_id": 11,
            "in_or_out": 22
        }
    })
    .done(function(responsedata) {

      // var parsedJson = $.parseJSON(responsedata);
      var parsedJson = {};
      parsedJson.result = 1;

      if(parsedJson.result==1){
        resolve(responsedata);
      }
      else{
        alert(parsedJson.title+"\n"+parsedJson.description);
      }
    })
    .fail(function(xhr, status, errorThrown) {
      console.log(status);
    });
  });
}

function thirdAJAX(data) {
  return new Promise((resolve, reject) => {
    $.ajax({
      type:"GET",
      url:"/qrcode/zero/",
      data:{
          "booking_id": 222,
          "token":"token"
      }
    })
    .done(function(responsedata) {    //Innermost done
      // var parsedJson = $.parseJSON(responsedata);
      var parsedJson = {};
      parsedJson.result = 1;

      if(parsedJson.result==1){
          alert("進場成功! 請使用易停網APP繳費與出場.");
          // window.location.href = "/download";
          resolve(responsedata);
      }
      else{
          alert(parsedJson.title+"\n"+parsedJson.description);
      }
    })
    .fail(function(xhr, status, errorThrown) {
        console.log(status);
    });
  });
}

async function loginAndEnter() {
  const first = await firstAJAX();
  const second = await secondAJAX(first);
  const third = await thirdAJAX(second);
  $("#login-and-enter-btn").prop('disabled', false);
  $("#login-and-enter-btn").text('登入和升起柵欄進場');
}

So the way it works is that loginAndEnter function will wait for firstAJAX, secondAJAX and thirdAJAX to be resolved. All of these functions return Promises, which are resolved when the GET request successfully receives a response. secondAJAX and thirdAJAX accept one parameter, which is the response passed asynchronously (thanks to 'await') from the function called before it.

I changed many values for my own testing purposes so please change them back to yours before trying it out.

Upvotes: 1

Related Questions