Fetch: Handling HTTP error statuses

Created on 9 Jan 2016  路  5Comments  路  Source: github/fetch

The docs recommend handling HTTP error statuses by throwing an error so the consumer can handle them within Promise.prototype.catch() -- https://github.com/github/fetch/tree/v0.10.1#handling-http-error-statuses

Whilst this is more familiar to people experienced with jQuery.ajax I'm not sure it's the best approach as, unlike jQuery.ajax, window.fetch uses es6 promises meaning runtime errors in the success handler will result in the catch handler being called. e.g.

window.fetch('https://api.github.com/users/richardscarrott/repos')
    .then(checkStatus) // throws if status is outside 200-299
    .then(parseJSON)
    .then(function success(data) {
        console.log('request succeeded with JSON response', data);
        data.foo.bar; // throws as foo is undefined
    }).catch(function error(error) {
        console.log('request failed', error); // error could either be a network or a runtime error
    });

I'm struggling to see how es6 promises can be used to express the semantics between a network error and a runtime error...

Most helpful comment

The promises tutorial describes the complexities of asynchronous error handling well.

The second argument to then is an error handling function that is invoked when the promise is rejected, as with a fetch network error.

var done = (response) => console.log(response)
var fail = (error) => console.log('fail', error)
Promise.reject(42).then(done, fail).catch(e => console.log('catch', e))
// => fail 42

Notice the catch handler was not invoked, because the second argument handled the error. If we didn't provide the fail function, the error would fall through into the catch handler.

When the promise is resolved with a fetch response, and the success handler throws an exception while handling the response, the fail function is not invoked, but the catch handler is.

done = (response) => { throw 'boom' }
Promise.resolve(42).then(done, fail).catch(e => console.log('catch', e))
// => catch boom

So we can use the second argument to then to deal with fetch network errors, and we can use the catch handler to deal with rendering errors.

The simple case, where we just handle successful fetches in then, and allow a catch block to handle network errors, non-200 responses, and bugs in 200 response handling code, is sufficient when we want to show the user a "This didn't work. Please try again." message regardless of its cause.

Hope that helps!

All 5 comments

That section of the documentation is less of a recommendation and more of an acknowledgement that many, probably most, people beginning to use fetch are switching from jQuery. The pattern described there is what we've used at GitHub to transition our legacy jQuery usage over to fetch.

Most fetch calls don't need to distinguish between a network failure and an error code from the server. For those that do, we use something like this:

fetch('/').then(checkStatus).then(function(response) {
  // Success handler.
}).catch(function(error) {
  // Treat network errors without responses as 500s.
  const status = error.response ? error.response.status : 500
  if (status === 404) {
    // Not found handler.
  } else {
    // Other errors.
  }
})

Error objects sent to the catch handler without a response property signal a network failure that was never able to receive a response from the server.

@dgraham I'm not concerned about distinguishing between a network error and an error code from the server, I'm concerned about distinguishing between an error code from the server and a runtime error in the success handler (the latter should bubble up to window.onerror).

To be fair, this is perhaps a topic for Stack Overflow more than here as it's re: window.fetch, not this polyfill, but I wanted to get your thoughts on it as I've recently come across an app which swallows rendering errors as a result of copy and pasting the checkStatus code from this readme.

Copy and paste in the grand JavaScript tradition!

The promises tutorial describes the complexities of asynchronous error handling well.

The second argument to then is an error handling function that is invoked when the promise is rejected, as with a fetch network error.

var done = (response) => console.log(response)
var fail = (error) => console.log('fail', error)
Promise.reject(42).then(done, fail).catch(e => console.log('catch', e))
// => fail 42

Notice the catch handler was not invoked, because the second argument handled the error. If we didn't provide the fail function, the error would fall through into the catch handler.

When the promise is resolved with a fetch response, and the success handler throws an exception while handling the response, the fail function is not invoked, but the catch handler is.

done = (response) => { throw 'boom' }
Promise.resolve(42).then(done, fail).catch(e => console.log('catch', e))
// => catch boom

So we can use the second argument to then to deal with fetch network errors, and we can use the catch handler to deal with rendering errors.

The simple case, where we just handle successful fetches in then, and allow a catch block to handle network errors, non-200 responses, and bugs in 200 response handling code, is sufficient when we want to show the user a "This didn't work. Please try again." message regardless of its cause.

Hope that helps!

To be honest I had never considered .then(done, fail) behaving differently to .then(done).catch(fail) but now you've explained it, it makes sense and is exactly what I'm after -- it would be the same as doing:

var req = fetch()
    .then(checkStatus)
    .then(parseJson);

req.then(done);
req.catch(fail);

Personally I feel it would be better for the docs to show an example with .then(done, fail) to demonstrate handling network errors codes but perhaps thats just my preference.

Thanks for your help!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gkatsanos picture gkatsanos  路  4Comments

fczuardi picture fczuardi  路  3Comments

ccorcos picture ccorcos  路  3Comments

AllenFang picture AllenFang  路  5Comments

kocur4d picture kocur4d  路  3Comments