Home > Blockchain >  What is the proper way to handle errors in RxJS?
What is the proper way to handle errors in RxJS?

Time:11-03

RxJS presents a lot of ways to handle errors, and I'm having a hard time understanding each of them and where they're used. The more I learn about RxJS, the less I understand it.

If I'm using a pipe, there's catchError.

If I'm using tap inside the pipe, there's the optional error parameter.

If I'm using subscribe, there's also the optional error parameter.

Where do I want to use one error handler over the other? What are the use cases for the optional error parameters? If I'm using tap's error parameter, do I still need to use catchError? If I have catchError, do I still need to use subscribes error parameter?

I want to better understand each of the error handlers, and what the proper way to go about error handling is.

CodePudding user response:

Handling the error using the subscribe error callback can do your job but it has limits in case you want to emit an alternative value in case of an error. (replacement value)

For example imagine that you are waiting for your observable to emit a list of users but you want to get an empty array in case of an error, you can't do that in the error callback inside your subscribe call but you can do it using catchError

getUsers$.pipe(
  catchError(error => of([]))
)

You use multple catchError operator to throw an error and add a replacement value

 getUsers$.pipe(
   catchError(error => {
     return throwError(error);
   }),
   catchError(error => {
     return of([]);
   })
  ).subscribe(
    users=> console.log('users', users),
    err => console.log('Error', err),
  )
    

that case the thrown error will never reach the subscribe handler function, you will have this in your console log

users []

CodePudding user response:

RxJS streams are designed in the way an error causes a collapse of the whole stream. To prevent this behaviour RxJS provides a catchError operator. It simply catch an error; input stream of catchError collapsed, but output stream of catchError is preserved (if catchError does not throw error too).

Example: Handled API error

actions$.pipe(
  filter(action => action.type === 'fetchData'),
  mergeMap(() => fetchData().pipe(
    map(data => ({ type: 'dataFetchSuccess', data })  
    catchError(error => of('dataFetchError'))
  ))
)

Note: When the fetchData fails, the actions$ stream doesn't fail and is able to process next value and fetch the data again.


Operator tap is designed to use for side effects, it has no effect on the stream. In other words it does not change the stream in any way.

Example: Operator tap is able to perform side effect for all cases: next, error and complete.

actions$.pipe(
  tap(console.log, console.error, () => console.log('complete')),
  // or
  tap({
    next: console.log,
    error: console.error,
    complete: console.log('complete'),
  }),
)

Error handler in subscribe is called if stream has collapsed due to an error. (Note: Practically catchError operator subscribes to input stream with an error handler, watches and processes the error.)


Conclusion: For simple stream that could be collapsed due to an error we can subscribe to the stream with error handler.

Use catchError for complicated stream that cannot be collapsed entirely.

  • Related