2

I want to know how to abort a request.

For example, I issue App.MyModel.find() and later i want to cancel/abort it before it returns from server. I would expect something like this:

var res = App.MyModel.find();   //request sent to server
res.abort();                    // abort the request before completion

but this doesn't work - the returned object is a promise and there is neither abort nor cancel methods.

Clarification

I'm looking for how to call the abort method on the underlying XMLHttpRequest object.

Shimon Rachlenko
  • 5,469
  • 40
  • 51

3 Answers3

8

For those of you that wonder how to do it, here is how I acheived to cancel a jquery ajax request.

First, I defined a new method in my Application store that will call cancelQuery on my custom RESTAdapter.

App.Store = DS.Store.extend({
  cancelQuery: function(type){
    var adapter = this.adapterFor(this.modelFor(type).modelName);
    if(typeof adapter.cancelQuery === 'function'){
      adapter.cancelQuery();
    }
  }
});

In my custom RESTAdapter, I define that new function and override ajaxOptions like this :

App.YOURMODELAdapter = DS.RESTAdapter.extend({
  jqXHRs: [],
  ajaxOptions: function(url, type, hash) {
    // Get default AjaxOptions
    var ajaxOptions = this._super(url, type, hash);

    // If the function was defined in the DS.RESTAdapter object,
    // we must call it in out new beforeSend hook.
    var defaultBeforeSend = function(){};
    if(typeof ajaxOptions.beforeSend === 'function'){
      defaultBeforeSend = ajaxOptions.beforeSend;
    }
    ajaxOptions.beforeSend = function(jqXHR, settings){
      defaultBeforeSend(jqXHR, settings);
      this.jqXHRs.push(jqXHR); // Keep the jqXHR somewhere.
      var lastInsertIndex = this.jqXHRs.length - 1;
      jqXHR.always(function(){
        // Destroy the jqXHRs because the call is finished and 
        // we don't need it anymore.
        this.jqXHRs.splice(lastInsertIndex,1);
      });
    };

    return ajaxOptions;
  },
  // The function we call from the store.
  cancelQuery: function(){
    for(var i = 0; i < this.jqXHRs.length; i++){
      this.jqXHRs[i].abort();
    }
  }
});

Now, you can just call cancelQuery in the context of a controller.

this.store.cancelQuery('yourmodel');
Nico
  • 6,395
  • 4
  • 25
  • 34
  • 1
    This is a decent solution and works well in situations where you want to kill all outgoing connections, like during 'resetController' One issue is that aborting a jqXHR will actually trigger the fail handler and cause an error at the route. This can be alleviated by adding an `ajaxError` handler that checks the statusText != "abort" as described here: http://paulrademacher.com/blog/jquery-gotcha-error-callback-triggered-on-xhr-abort/ – Alexandros K Jan 27 '15 at 17:27
  • Nice catch ! If you do override the error handler, make sure to call the default error handler if the status != 'abort'. I used a similar trick with the `beforeSend` handler. – Nico Jan 27 '15 at 18:19
  • Yes indeed. As well, I'm not exactly sure but the `this.jqXHRs` might not work as expected if `beforeSend` is called outside of the adapter context. It might be prudent to create a closure for that outside the returned object. (e.g. `jqXHRs = this.jqXHRs` outside the `ajaxOptions.beforeSend` function. – Alexandros K Jan 29 '15 at 14:37
  • Overriding error in the ajaxOptions function doesn't seem to do much since it will be overridden inside of the RESTAdapters ajax function. – prashn64 Oct 28 '15 at 01:03
  • Hi, this code is working with ember v1.8.1 and ember-data beta19. They made major change since then so if it's not working on your version, maybe you can take the code and adapt it to your need. – Nico Oct 28 '15 at 17:54
0

I believe, when you call App.MyModel.find(), it's going to return you a RecordArray, not a promise.

From looking at the ember-data source code for RESTAdapter, I don't think there's a way to call abort on your XMLHttpRequest object.

You can implement your own RESTAdapter:

var myRESTAdapter = DS.Adapter.create({
  find: function (store, type, id) {
    var req = $.ajax({
      url:      type.url,
      dataType: 'jsonp',
      context:  store,
      success:  function(response){
        this.load(type, id, response.data);
      }
    });
    req.abort();
  },
  findAll: function(store, type) {
    var req = $.ajax({
      url:      type.url,
      dataType: 'jsonp',
      context:  store,
      success:  function(response){
        this.loadMany(type, response.data);
      }
    });
    req.abort();
  }
});

and call abort in XMLHttpRequest object.

Alex Navasardyan
  • 536
  • 5
  • 12
-1

Http is request-response interaction.Once the request is sent,it can't be canceled.You can fine some way to ignore the response from the server instead;

  • Please see my clarification. – Shimon Rachlenko Feb 17 '13 at 10:49
  • 1
    The Promise object (RSVP) should allow for a way to cancel the request and trigger a reject or failure event. – rxgx Oct 10 '13 at 21:07
  • Lol, what do you mean a HTTP request can't be aborted? Why do you assume the request makes it to the server in the first place? Also, you can definitely cancel a HTTP request even while it is actively being processed on the server (think streaming). The OP question is very legit for cleanup, user abort, or route switching / controller reset scenarios. – Adrian Crețu May 03 '18 at 23:46