Skip to content

Instantly share code, notes, and snippets.

@jamesplease
Last active June 22, 2017 15:23
Show Gist options
  • Save jamesplease/c2f1117c87593b142e01e435cfff3874 to your computer and use it in GitHub Desktop.
Save jamesplease/c2f1117c87593b142e01e435cfff3874 to your computer and use it in GitHub Desktop.
Canceling XHR requests

tl;dr

Always cancel your XHR requests before firing a new one. It solves lots of bugs.

How to do it

The native XHR object has an abort method. Use a library that either gives you access to the xhr object, or wraps it with some other system for aborting.

Then,

  1. Attempt to cancel the last request before firing a new one whenever the user can initiate an action (such as clicking a button to fetch data)
  2. Cancel all requests for a component in componentWillUnmount

Why

When two requests with the same "purpose" are sent at once, they may not return in the same order they were sent. You can write code to keep track of the requests, but in my experience it takes less code to cancel the old requests instead.

Example

See below

class MyComponent extends Component {
render() {
// Render things
}
constructor(props) {
super(props);
this.xhrs = {};
}
componentWillUnmount() {
// When this thing unmounts, we _usually_ don't want any of its requests coming back and changing the store's state
_.invokeMap(this.xhrs, 'abort');
}
onSomeUserAction = () => {
// Alright, cancel this request if it already exists and is in flight
_.invoke(this.xhrs.someRequest, 'abort');
// Fire off a new one! `doSomething` should be an action creator that returns the native XHR object
this.xhrs.someRequest = doSomething();
}
}
@tbranyen
Copy link

tbranyen commented Jun 22, 2017

Here is an idea for an "abortable fetch":

// Abortable fetch Promise
function makeRequest(opts) {
  const req = fetch(opts).then(resp => {
    if (req.aborted) {
      // Abort == `null` error in reject handler.
      return Promise.reject(null);
    }

    return resp;
  });

  req.abort = () => (req.aborted = true);

  return req;
}

const req = makeRequest(opts);

req.catch(ex => {
  // Now in your action creators or wherever we make fetch happen, ensure to catch and ignore null
  if (ex !== null) {
    throw ex;
  }
});

// Abort anytime...
req.abort();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment