#302 enhancement
Mark Caudill

Ajax.abort method

Reported by Mark Caudill | August 25th, 2008 @ 02:16 AM | in 1.7

Aborting an ajax connection (and call) can be very handy, especially when the user is leaving the page. I've made a patch which should be correct.

I don't know if there are any other unforeseen implications, but this should be fail-safe. Anyway, let the code speak for itself (if everything is correct that is :)).

Comments and changes to this ticket

  • John-David Dalton

    John-David Dalton August 25th, 2008 @ 02:59 AM

    Here is my attempt at the abort method, I have some unit tests as well: http://github.com/jdalton/prototype/commit/f644416bf90d4fb9d6482bc108d4e1d24b2d95b2

    
    
    // I donno why we "_" underscore the "complete" variable,
    // usually its to mark it as intended to be private, but
    // why even mark it as such ?
    
    Ajax.Request = Class.create(Ajax.Base, {
       _complete: false,
      aborted: false,
    
    //...
    
      abort: function() {
        if(this._complete) return;
    
        // avoid MSIE/Mozilla calling other event handlers when aborted
        this.transport.onreadystatechange = Prototype.emptyFunction;
        this.transport.abort();
        this._complete = true;
        this.aborted = true;
    
        var response = new Ajax.Response(this);
    
        ['Abort', 'Complete'].each(function(state) {
          try {
            (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
            Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
          } catch (e) {
            this.dispatchException(e);
          }
        }, this);
      }
    // ...
    
  • Mark Caudill

    Mark Caudill August 25th, 2008 @ 02:48 AM

    Adding just the ajax.js diff patch (was prototype.js diff patch).

  • John-David Dalton

    John-David Dalton August 25th, 2008 @ 07:08 AM

    • Tag changed from ajax, enhancement, patched to ajax, enhancement, needs_tests, patched

    More info on ajax and abort: http://www.quirksmode.org/blog/archives/2005/09/xmlhttp_notes_a_1.html

    
    @Mark
    
    Why do you wrap your abort method in a try-catch statement?
    
    The other difference is my patch dispatches the "abort" and "complete"
    ajax responders, but does not dispatch the success or failure responders.
    
    I think some discussion is needed to iron out if the "complete" responder should be fired.
    Also I don't think you need to have any modifications to the "respondToReadyState"
    method because we clear the "onreadystatechange" handler that calls it.
    
  • John-David Dalton

    John-David Dalton August 25th, 2008 @ 03:00 AM

    • Milestone set to 1.7
    • State changed from “new” to “enhancement”
    • Assigned user set to “John-David Dalton”
  • Mark Caudill

    Mark Caudill August 25th, 2008 @ 03:09 AM

    • Assigned user cleared.

    I've attached a patch that is more or less yours without the onComplete.

    @John, I actually added the try-catch statement at the last second because I was unsure if this.transport.abort() could ever return an error.

    I'm sure this needs further testing/modifying, but I don't think onComplete should trigger when something is aborted (given up on). I do like that you added onAbort though.

  • Mark Caudill

    Mark Caudill August 25th, 2008 @ 03:10 AM

    • Assigned user set to “John-David Dalton”
  • John-David Dalton

    John-David Dalton August 25th, 2008 @ 07:09 AM

    Garrett fires onAbort and then onComplete: http://www.dhtmlkitchen.com/ape/docs/AsyncRequest.src.html

    
    I think that complete just means the ajax call has been completed,
    and when the ajax call is aborted its call is completed,
    it did not execute successfully but it did finish the operation.
    
  • Mark Caudill

    Mark Caudill August 25th, 2008 @ 03:28 AM

    Patch had spacing issues, here's another one.

  • Mark Caudill

    Mark Caudill August 25th, 2008 @ 03:33 AM

    Well, the big question is whether it completed. From a developer perspective, the application is already aware the call was aborted (since it was initiated).

    I also think that the widespread abuse of onComplete could cause more problems than allowing them to just set the onAbort to the same thing as onComplete if the need be (which seems unlikely in most cases).

  • John-David Dalton

    John-David Dalton August 25th, 2008 @ 07:10 AM

    
    onComplete is currently called when the ajax request fails as well.
    
  • Mark Caudill

    Mark Caudill August 25th, 2008 @ 03:44 AM

    In that situation where the request actually fails, the application isn't aware of the failure. The request did complete it's lifecycle, it just wasn't able to successfully get a response. In the case of aborting, the application is aware of the abort and the request did not get to complete it's lifecycle because it was stopped manually.

    Do you know what the response status is upon aborting?

  • John-David Dalton

    John-David Dalton August 25th, 2008 @ 07:10 AM

    
    What I meant to say is that when a failure occurs the "onFailure" fires as well
    as the "onComplete" so the people abusing "onComplete" assuming its the same
    as "onSuccess" will still have issues.
    
    I do not know what the response status is,
    maybe the article I linked to gave that info.
    
  • Mark Caudill

    Mark Caudill August 25th, 2008 @ 04:10 AM

    If the readyState is greater than 1 there is a status code (which could be 200). If the readyState is 1 or 0, I believe the status code would not be available.

    I don't think that onComplete should be called for this reason. It may send a status 200 when the actual contents aren't there (readyState 4 or >2 for non-IE)

    Ajax.Response takes this all into account and does not try to get the status for 1 or 0:

    
    Ajax.Response = Class.create({
      initialize: function(request){
        this.request = request;
        var transport  = this.transport  = request.transport,
            readyState = this.readyState = transport.readyState;
    
        if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
          this.status       = this.getStatus();
          this.statusText   = this.getStatusText();
          this.responseText = String.interpret(transport.responseText);
          this.headerJSON   = this._getHeaderJSON();
        }
    
        if (readyState == 4) {
          var xml = transport.responseXML;
          this.responseXML  = Object.isUndefined(xml) ? null : xml;
          this.responseJSON = this._getResponseJSON();
        }
      }
    
  • John-David Dalton

    John-David Dalton August 25th, 2008 @ 07:08 AM

    
    because the onreadystatechange handler is changed to an emptyFunction
    when we abort there is no risk of a false onSuccess being fired.
    I don't think it matters what the status is when you abort.
    
  • Mark Caudill

    Mark Caudill August 25th, 2008 @ 04:54 AM

    I understand there's no risk of onSuccess firing. What I'm saying is that likely most onComplete receiver functions are going to base what they do on the status of the response (200 for success, 404 for not found, etc.).

    This means on non-IE based browsers, onComplete may get a 200 status if they abort it while readyState is 2 or 3. That should be reason enough not to call onComplete in my opinion.

  • John-David Dalton

    John-David Dalton August 25th, 2008 @ 07:07 AM

    
    I don't think they will be doing that,
    if they want to know if a call is successful then they use onSuccess.
    Or we can fake the status in the Responder class if it detects that .aborted is true.
    
  • Mark Caudill

    Mark Caudill August 25th, 2008 @ 05:04 AM

    It just seems rather flimsy to base using onComplete rather than not using onComplete just on that people shouldn't be using onComplete. If they shouldn't be using it, it shouldn't be available to use.

    If onComplete is called on abort, it definitely will result in bugs being submitted that Prototype isn't handling abort correctly because onComplete is getting a status 200 with partial data even though the call was aborted.

  • John-David Dalton

    John-David Dalton August 25th, 2008 @ 07:29 AM

    
    We can fake the status in the Responder class if necessary.
    
    From the docs:
    "onComplete Triggered at the very end of a request's life-cycle, once the request completed,
    status-specific callbacks were called, and possible automatic behaviors were processed."
    
    OnComplete does not guarantee a successful request.
    There is the .aborted property that devs can use to further check.
    
    I think onComplete is used to execute code when the lifecycle has ended regardless
    of the success / failure / or abort state of the request.
    

    For example:

    
    onComplete: function(transport) {
      $('loading').hide();
    }
    
    // Even onAbort the loading indicator would still be hidden.
    
    
    I think "once the request is completed" covers the case of "abort".
    Lets wait and see what others think.
    
  • Tobie Langel

    Tobie Langel August 25th, 2008 @ 08:01 AM

    For reference: http://www.w3.org/TR/XMLHttpRequ...

    Note that the try catch statement is necessary here.

    Also, Ajax.Request#abort is needed to implement onTimeout or something like that.

  • John-David Dalton

    John-David Dalton August 25th, 2008 @ 03:09 PM

    
    @Tobie why is the try catch necessary?
    The reference you pointed to does not mention anything about an error being thrown.
    
    jQuery uses the ajax abort method in several places without using try-catch.
    
    
  • Tobie Langel

    Tobie Langel August 25th, 2008 @ 03:12 PM

    I wasn't implying a relationship between the link and the try-catch statement.

    Re try-catch, it's because we are using onreadystatechange callback. Other libs don't, they simply poll the status property.

  • John-David Dalton

    John-David Dalton August 25th, 2008 @ 03:28 PM

    
    In the abort method we set onreadystatechange to an emptyFunction.
    In my unit tests, I had no problem with not using a try-catch.
    The quirksmode link above has some more info on using an empty
    function with onreadystatechange as well.
    
  • Mark Caudill

    Mark Caudill August 25th, 2008 @ 04:16 PM

    @John, Since the onAbort callback was added, the try...catch would be required anyway. I don't think there is a performance loss of having more inside a try...catch. If we use an onComplete then we will have to check to see if there are performance losses, so the onComplete is still the big variable.

    @Tobie, What do you think about onComplete not being or being called? (Keeping in mind we'd have to make the status an artificial status number, like -1.)

  • John-David Dalton

    John-David Dalton August 25th, 2008 @ 05:01 PM

    
    @Mark - Tobie pointed out that the try-catch
    is needed when you attempt to abort a request that
    has timed-out, so its coo with me :).
    
    It wasn't really about performance,
    I just think its good to avoid extra complexity if its not needed.
    
  • Mark Caudill
  • Mark Caudill

    Mark Caudill August 26th, 2008 @ 03:27 AM

    • Tag changed from ajax, enhancement, needs_tests, patched to ajax, enhancement, patched

    Added test patch for ajax and added check for _complete. May still need some functional tests.

  • Mark Caudill
  • Tobie Langel

    Tobie Langel August 26th, 2008 @ 09:09 AM

    This should be built together with the ontimeout callback.

  • Mark Caudill

    Mark Caudill August 26th, 2008 @ 05:09 PM

    Added the onTimeout callback and functionality. To use: Add timeout (seconds) to the options. The timeout will execute in x seconds. Upon calling the timeout, onTimeout callback is called and then abort is called. (Unless abort should be called first? Does it matter?) If it is aborted or completed before the timeout (and somehow the timeout isn't cleared), it won't execute the onTimeout callback.

  • Tobie Langel
  • Tobie Langel

    Tobie Langel August 26th, 2008 @ 05:18 PM

    • Assigned user changed from “John-David Dalton” to “Tobie Langel”
    • Tag changed from ajax, enhancement, patched to ajax, needs_tests, patched

    A year old implementation, not tested in IE.

    Have a look at the unit tests in there.

    It would be useful to discuss the API for this

  • Mark Caudill

    Mark Caudill August 26th, 2008 @ 06:25 PM

    Added some fixes based on the year old implementation uploaded by Tobie (Responder added). The similarities between his and mine are very close, with some distinction in how the the timeout is handled.

    I changed onTimeout to respondToTimeout for convention (as seen in the patch Tobie uploaded).

    I think the best functionality as far as the API (which this patch should allow fault tolerance for) is having them set timeout = sec, onTimeout: func(){};

  • John-David Dalton

    John-David Dalton August 26th, 2008 @ 06:49 PM

    I think there still needs to be an onAbort dispatched. Also I think onComplete should be called after the onAbort and onTimeout. A common use for onComplete is to hide ajax loading indicators:

    
    onComplete: function(transport) {   $('loading').hide(); }
    

    In both Tobie's and Mark's patches this will not fire when the request timesout or is aborted.

  • Mark Caudill

    Mark Caudill August 26th, 2008 @ 07:39 PM

    I think John does have a point that onComplete is commonly used for simple loader hides or "resets" of things initiated before the call.

    Here are some things we need to decide if we do use onComplete as a callback for aborting/timeouts:

    1) There is no HTTP status code representational of abort() so we would have to use the default (0) or something like -1 (or a string "aborted").

    2) For timeouts, I don't think there is a valid status code for client side timeout so we would have the same situation of using 0 or -1 (or a string "timedout").

  • John-David Dalton
  • John-David Dalton

    John-David Dalton August 26th, 2008 @ 08:07 PM

    Here are a list of other http status's from IIS, several for aborted connections: http://support.microsoft.com/kb/318380

    
    426 - Connection closed; transfer aborted.
    

    Because this Microsoft http status code is not standard I would also support a -1 status code.

  • Mark Caudill

    Mark Caudill August 26th, 2008 @ 08:40 PM

    426 Connection closed; transfer aborted. - this would probably fit best, but HTTP status codes are provided by the server. Spec: http://www.w3.org/Protocols/rfc2....1.1

    I think 0 or -1 is the most logical as the request never received a valid HTTP status code. With this, maybe we should add a timedout value to the response (bool) which upon a status code of -1 can be checked.

  • Tobie Langel

    Tobie Langel August 26th, 2008 @ 10:03 PM

    JDD, those are server side HTTP statuses. We're talking about the case when we actually don't have a response at all.

  • Mark Caudill

    Mark Caudill August 26th, 2008 @ 11:17 PM

    Another new patch, this time with -1 for status code on Timeout/Abort and statusText with "Timedout" or "Aborted"

    @Tobie, What other API things need to be sorted out?

  • Tobie Langel

    Tobie Langel August 27th, 2008 @ 11:00 AM

    custom status codes seems to be a dangerous path to walk in. Furthermore, two different status texts for the same status code seems to break the one to one relationship enforced by the spec.

  • Tobie Langel

    Tobie Langel August 27th, 2008 @ 11:03 AM

    • Tag changed from ajax, needs_tests, patched to ajax, needs_tests

    Again, I'd encourage you to look at the specs where the expected behaviour is clearly specified.

  • Mark Caudill

    Mark Caudill August 27th, 2008 @ 09:27 PM

    I've added some code to #308 that accomplishes this plus many other things. I know it's simpler to keep things separate, but the Ajax section of Prototype needs some major work that will not get done any time soon without a "round up."

  • Jr. Hames

    Jr. Hames September 17th, 2008 @ 10:26 PM

    • Tag changed from ajax, needs_tests to ajax, needs_tests

    After several minuts trying to get yours patchs to work, I made one based on patch of Mark Caudill that works. But, how its suppose to work? With onAbort handler? Simply aborting code before calling onSuccess? or Calling onSuccess with the response status equals to zero?

    Thanks

  • Ecke

    Ecke September 26th, 2008 @ 12:44 PM

    I've been testing calling an abort method on the Ajax transaction but even though it works, it doesn't actually free up the connection. If you set 3 simulateneous requests off, each one to a php page with a 10 second sleep in it, then cancel one of the first 2 request, even though it does abort, the 3rd request cannot fire until IE recieves a response from the cancelled request.

    Obviously this is because IE7 only supports 2 concurrent http requests, but i would have expected the abort to free one of them up.

    Does anyone have any ideas how to get round this?

    Thanks

  • Tobie Langel

    Tobie Langel July 24th, 2009 @ 01:54 AM

    • Tag changed from ajax, needs_tests to needs_tests, section:ajax

    [not-tagged:"ajax" tagged:"section:ajax" bulk edit command]

  • Tobie Langel

    Tobie Langel July 24th, 2009 @ 02:26 AM

    • Tag changed from needs_tests, section:ajax to missing:tests, section:ajax

    [not-tagged:"needs_tests" tagged:"missing:tests" bulk edit command]

  • Tobie Langel

    Tobie Langel July 24th, 2009 @ 03:36 AM

    • Tag changed from missing:tests, section:ajax to needs:tests, section:ajax

    [not-tagged:"missing:tests" tagged:"needs:tests" bulk edit command]

  • T.J. Crowder

    T.J. Crowder November 16th, 2009 @ 04:50 PM

    • Assigned user cleared.

    [responsible:none bulk edit command]

Please Sign in or create a free account to add a new ticket.

With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

The Prototype JavaScript library.

Shared Ticket Bins

Referenced by

Pages