简体   繁体   中英

How to abort pending jquery ajax request when user navigates to other page?

I have defined a method to send jquery ajax request like this

sendAjaxRequest(URL, data) {
    return $.ajax({
        type        : 'POST',
        url         : URL,
        crossDomain : true,
        data        : JSON.stringify(data),
        dataType    : 'json'
    }).fail((responseData) => {
        if (responseData.responseCode) {
            console.error(responseData.responseCode);
        }
    });
}

From the place of calling I keep the reference of ajax call

this.ajaxRequest = sendAjaxRequest(Url, data);

and based on user navigation if user navigates away from page ( goes to another page on same website via router ) I want to cancel any pending ajax request.

From the docs I found that jquery jqXHR object has an abort method to cancel any ajax request. But in my ajaxRequest object I don't have any such method available. I am using jquery 2.2 .

For clarification I also want to add that I am using react , and want to achieve this.

  componentDidMount: function() {
    this.serverRequest = $.get(this.props.source, function (result) {
      var lastGist = result[0];
      this.setState({
        username: lastGist.owner.login,
        lastGistUrl: lastGist.html_url
      });
    }.bind(this));
  },

  componentWillUnmount: function() {
    this.serverRequest.abort();
  },

Taken from this link .

They basically say that When fetching data asynchronously, use componentWillUnmount to cancel any outstanding requests before the component is unmounted.

I must be doing something wrong. Any help is appreciated.

According to the ajax documentation , you're doing it properly. The $.ajax() function returns an object that has an abort function. You should check for existence of the function because it might not exist if the request has already finished.

componentWillUnmount: function(){
  if (this.ajaxRequest && this.ajaxRequest.abort){
    this.ajaxRequest.abort()
  }
}

http://api.jquery.com/jquery.ajax/#jqXHR

Please don't try to abort the AJAX request in this case. The better way is not to do anything if the component is unmounted . Take a look at this:

componentDidMount: function() {
  // Register to the handler
  this._requestSuccess = this.requestSuccess.bind(this); 
  $.get(this.props.source, this.requestSuccess);
},

componentWillUnmount: function() {
  // "Unregister" by assign it to an empty function
  this._requestSuccess = () => {};
 },

requestSuccess(lastGist) {
  // Do your work
  this.setState({
    username: lastGist.owner.login,
    lastGistUrl: lastGist.html_url
  });
},

In the big picture, not just Ajax requests but any async operation inside a component that may change the component's states should be register in componentDidMount and unregister in componentWillUnmount , or else you'll find yourself get lost async spaghetti mess.

In the above example, I used this._requestSuccess = ...; to register and this._requestSuccess = () => {}; to unregister the handler, but in real world application, you'll see people use Flux or Redux to do the same thing.

I highly encourage you to use one of those to manage your application data flow rather than just sending and aborting ajax requests everywhere.

Just have a look on the fiddle . Run the fiddle and check the network tab.

 $('button').on('click', function() { var _ajax = $.ajax({ type: 'POST', url: "yourURL", crossDomain: true, data: JSON.stringify('hello'), dataType: 'json' }).fail((responseData) => { if (responseData.responseCode) { console.error(responseData.responseCode); } }); _ajax.abort(); }); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script> <button> Click me </button> 

The ajax variable will hold all the ajax related functions as shown in the documentation .

If componentWillUnmount is not called have a look at your router configuration.

<Route path="/" component={App}>
  <IndexRoute component={Home}/>
  <Route path="invoices/:invoiceId" component={Invoice}/>
  <Route path="accounts/:accountId" component={Account}/>
</Route>

In this example the {App} component is never unmounted since it's at the root. When you navigate from /invoices/ to /invoices/123 the Invoices component is not unmounted either.

Alternatively you can also tie in to the routers setRouteLeaveHook like this

import React from 'react';
import { withRouter } from 'react-router';

const MyComponent = React.creatClass({

  componentDidMount() {
    this.request = $.get( /* ... */ );

    this.props.router.setRouteLeaveHook(this.props.route, (request) => {
      request.abort();
      return true;
    });
  }

/* .... */

});


export default withRouter(MyComponent);

withRoute became available in react-router 2.4.0

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM