How to Distinguish a User-Aborted AJAX Call from an Error

Let’s say you’re writing a LOLcats app and you want to be all user-friendly and show an adorable little kitty-cat error message whenever an AJAX call fails. So you write an error handler using your favorite cross-browser library (mine is jQuery), something like this:

  $.ajax( { url: "someUrl", success: function() {
    // do something impressive with the results
  }, error: function() {
    showError("Ohs noes. Tell me when you fix your AJAX.");
  } } );

But you start using your app and you notice that your error message also appears when you navigate away from the page before an AJAX call has finished, or when you hit Escape to cancel the AJAX call. That kind of sucks—it looks ugly to have the error message appear when there really was no error.

I did a little looking around and found what looks like a cross-browser-friendly way to tell if an error really occurred or if something else happened, like the user navigating away or hitting Escape.

The trick is to check the response headers in the XMLHttpRequest object. If there are no response headers (null or empty string, depending on the browser), the server did not respond yet. That means the user aborted.

Here’s a function that takes an XMLHttpRequest and tells you if the user aborted it.

  /**
   * Returns true if the user hit Esc or navigated away from the
   * current page before an AJAX call was done. (The response
   * headers will be null or empty, depending on the browser.)
   *
   * NOTE: this function is only meaningful when called from
   * inside an AJAX "error" callback!
   *
   * The 'xhr' param is an XMLHttpRequest instance.
   */
  function userAborted(xhr) {
    return !xhr.getAllResponseHeaders();
  }

And here is the updated application code to call this new function. You can easily adapt this to another JavaScript library than jQuery:

  $.ajax( { url: "someUrl", success: function() {
    // do something impressive with the results
  }, error: function(xhr, textStatus, errorThrown) {
    if (!userAborted(xhr)) {
      showError("Ohs noes. Tell me when you fix your AJAX.");
    }
  } } );

Oh, did that “textStatus” and “errorThrown” catch your eye? I already looked at those. You can’t use them to tell if the user aborted. They return the same values whether the user aborted or the server returned with an error. (I only tested a 500 error.)

17 thoughts on “How to Distinguish a User-Aborted AJAX Call from an Error”

  1. Brian,
    What if the request fails due to connectivity issues?
    let’s say the user losses internet access or the server crashes. The response headers will still be empty, and the code will consider the request as aborted by user, wouldn’t it?

  2. The way I found to get over this is checking the status text of the xhr object.
    If the request was aborted the text will be *abort*, while if the request failed it will be *error*

    so this code does the trick:

    xhr.statusText == “abort”;

    1. @mohoch, Interesting. Did you test the xhr.statusText == “abort” method across all browsers? The jQuery documentation says the same is true of the second callback parameter, “textStatus.” But as I mentioned at the end of the post, when I ran tests (using whatever version of jQuery was current in 2009) I found that textStatus has the same value, regardless of whether the request was aborted by the user or due to an error.

      1. Well I checked Firefox, Chrome and IE8 and above.
        jQuery version is the latest.
        It is very likely that things ave changed over the years.
        And there is no documentation on jQuery’s site and I could hardly find anything on the web. Your post was the only one I could find addressing the problem at all.

        Thanks.

  3. In the refresh case I am getting xhr.statsText == “error” and cannot distinguish between a network disconnect and the refresh interuption. I tried chrome 22.0.1229.94 and firefox 16.02. In the chrome debugger they have a status text column and they show (cancelled) for the refresh and (failed) for a network error but I can’t seem to find a way to access these values from the $.ajax error or complete functions

  4. You can detect a forced abort by refresh or navigating away using window.beforeunload. Use this in combination with status and you can perfectly differentiate between a connection problem and a “forced” connection problem (refresh, navigating away).

  5. On Chromium Version 28.0.1500.71 Ubuntu 12.04 (28.0.1500.71-0ubuntu1.12.04.1) both (xhr.statusText == “error” && ! xhr.getAllResponseHeaders()) regardless if user navigates away, or whether I restart the web server during the AJAX call. So these two test are not reliable tests for whether the error is due to user actions.

Leave a Reply

Your email address will not be published. Required fields are marked *

Feel free to use <a>, <b>, <i>, <strong>, <em>, <strike>, <code>.

Code blocks:
[code language="ruby/javascript/html/css/sass/bash"]
[/code]