tinyCoder
tinyCoder

Reputation: 360

Chrome and Safari ajax issue with async:false

I saw this everywhere on the web but i couldn't manage to fix my code to avoid this issue, simply I have an ajax function that I trigger when some buttons are clicked, sometimes I want an indicator (loading animation) to show, sometimes not, so i build my function:

function doAjax(action, todo, items, error_num, hide_indicator) {
  items.action = action;
  items.do = todo;
  var postedObj;

  $.ajax({
    type: 'post',
    dataType: 'html',
    url: ajaxURL,
    data: items,
    async: false,
    beforeSend: function() {
      if (!hide_indicator) showIndicator();
    },
    success: function(data) {
      if (data) {
        ajaxObj = JSON.parse(data);
        if (ajaxObj.ok) {
          postedObj = ajaxObj;
        } else {
          alert(ajaxObj.error);
          postedObj = false;
        }
      } else {
        alert('[' + error_num + '] Something went wrong.');
        postedObj = false;
      }
      if (!hide_indicator) hideIndicator();
    },
    error: function() {
      alert('[' + error_num + '] Something went wrong.');
      postedObj = false;
      if (!hide_indicator) hideIndicator();
    }

  });
    return postedObj;
}

And here is what I do on my buttons to call the function:

$(document).on('click', '.ad-single', function() {
  var addataObj = doAjax('post_requests', 'get-ad-info', {"id": $(this).data('ad')}, '158', false); // false means DON'T hide the indicator
  if (addataObj) {
    loadContent(addataObj.ad);
  }  
});

OK, for now, everything works as expected on Firefox, when I click my button, the indicator shows up and wait until data is returned by ajax, then hide the indicator again.

This doesn't work on Chrome and Safari, the function works fine and return the data as expected but it seems that the hideIndicator() function is called immediately, I couldn't know how to fix this.

Firefox:

enter image description here

Chrome and Safari:

enter image description here

Upvotes: 2

Views: 10206

Answers (1)

31piy
31piy

Reputation: 23859

Using async: false is evil. The Internet is full of such posts, and many browsers including Chrome and Safari will give you numerous warnings on making synchronous AJAX requests.

Fortunately, jQuery's promises can be used to overcome your problem. It doesn't change how your function works, and leverages the power of promises too to make async requests.

You can modify your existing function as follows, so that it returns an AJAX promise, instead of the result.

function doAjax(action, todo, items, error_num, hide_indicator) {
  items.action = action;
  items.do = todo;

  return $.ajax({
      type: 'post',
      dataType: 'html',
      url: ajaxURL,
      data: items,
      beforeSend: function() {
        if (!hide_indicator) showIndicator();
      }
    })
    .then(function(data) {
      if (!hide_indicator) {
        hideIndicator();
      }

      if (data) {
        ajaxObj = JSON.parse(data);

        if (ajaxObj.ok) {
          return ajaxObj;
        }

        alert(ajaxObj.error);
        return false;
      }

      alert('[' + error_num + '] Something went wrong.');
      return false;
    }, function() {
      if (!hide_indicator) {
        hideIndicator();
      }

      alert('[' + error_num + '] Something went wrong.');
      return false;
    });
}

Here, a .then function can be used to pre-process the response, and return the appropriate value to the subsequent promise handlers.

A subsequent promise handler can be implemented as follows, which calls the doAjax function, and reacts on the value returns by pre-processing.

$(document).on('click', '.ad-single', function() {
  doAjax('post_requests', 'get-ad-info', {"id": $(this).data('ad')}, '158', false)
    .then(function(addataObj) {
      if (addataObj) {
        loadContent(addataObj.ad);
      }
    });
});

Here, the .then() chains up with the previous .then(), and reacts on the value returned (which is received in addataObj). Note that it only reacts on the success of the AJAX call. If you want to handle the error response as well, you need to pass a second argument as a function to the .then() handler.

Hope this solves your problem.

Upvotes: 5

Related Questions