Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Promise.catch is triggered on request unrelated error #400

Closed
malyzeli opened this issue Aug 4, 2016 · 6 comments
Closed

Promise.catch is triggered on request unrelated error #400

malyzeli opened this issue Aug 4, 2016 · 6 comments

Comments

@malyzeli
Copy link

malyzeli commented Aug 4, 2016

When you make axios request and error occurs anywhere in the code during the processing of request, catch error handler is triggered even when the error has nothing to do with the request and the request is successfully processed.

Not sure if it's related to specific libraries, but in my opinion the behaviour is not correct.
Involved libraries are:

  • react
  • react-redux
  • redux
  • redux-promise
  • redux-thunk

Flow is the following:

  1. make request on valid url
  2. server sends data in a response (200)
  3. client receives the data and pass it to reducer
  4. reducer updates the application state
  5. state update triggers component update and rerender
  6. component render throws an error due to missing data in updated state
  7. error is caught by axios catch error handler

This makes my application report incorrect error saying that there was problem with the request, but in fact the request was completed and the error was raised after that.

I discovered it in project where I write both client and server so I'm sure that there is no error in the request.

I'm quite new to JavaScript so please explain if I'm wrong, but I believe this is not desired behaviour of error handler.

@nickuraltsev
Copy link
Member

This is how promises work.

For example, the code below will print 'caught an error' even if the Promise returned by axios is resolved successfully:

const promise = axios.get('http://foobar.com/');
promise
  .then(() => {
    throw new Error('boom!');
  })
  .catch(err => {
    console.log('caught an error', err);
  });

The reason why the error handler is called is that catch is called on a new (rejected) Promise returned by then, rather than on the Promise returned by axios.

Hope this helps!

@malyzeli
Copy link
Author

malyzeli commented Aug 4, 2016

Thanks for the explanation, though I still don't really get it.

I understand your example, where an error is raised by code inside the callback, but I don't see the connection in my case, where the error happens in another part of application.

Can you recommend some approach how to easily distinguish between http request error and some other error? I don't see comparing strings as effective and error-prone way to achieve that.

I expect the catch clause handle http request errors only, while all other application errors should be handled in places they can eventually occur.

@nickuraltsev
Copy link
Member

The function passed to then updates the redux store and renders your React components (indirectly). If an error is thrown in render, it will be caught by catch:

const promise = axios.get('http://foobar.com/');
promise
  .then(() => {
    // This is what happens when the `Promise` returned by axios is resolved:
    // - dispatch action
    //   - update state
    //     - notify React components
    //       - render
  })
  .catch(err => {
    // If an error is thrown in `render`, the `Promise` returned by `then` will be rejected
    // and this error handler will be executed
  });

@malyzeli
Copy link
Author

malyzeli commented Aug 5, 2016

Now it makes perfect sense.
Thank you, Nick!

@vdh
Copy link

vdh commented Jul 20, 2017

This feels somewhat inelegant, but I found a workaround by wrapping dispatch in a setTimeout to prevent unrelated errors bubbling up into the request promise.

const promise = axios.get('http://example.com/');
promise
  .then((response) => {
    // Do stuff…
    setTimeout(() => {
      // Now this dispatch is not connected to the request promise…
      dispatch(successAction(response));
    });
  })
  .catch((error) => {
    // Now this only catches errors from the request, or other code outside the setTimeout call
  });

@sarink
Copy link

sarink commented Apr 16, 2018

The best way to handle this is to utilize the second argument to .then, which is an error callback:

promise.then(
  (successResponse) => { ... },
  (errorResponse) => { ... },
)

@axios axios locked and limited conversation to collaborators May 21, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants