The fact is if there is any of your stream breakdown with an error, the whole system can go wrong. Here is a simple example of how your system can go down hopelessly without any expectation:
functiondivide(a, b) {if (b ===0) {/** * Just in context of this example. Which might return Infinity in * reality for the sake of JS god. */thrownewError('Division by Zero') }return10/ b}number$.pipe(map(number =>divide(1, number))).subscribe(...)
That why handling error is so important in RxJS. Sadly, I haven't found any global way to declare error handler in React Epic. So in a long run, we have to handle error manually:
But handling errors the wrong way can be troublesome:
In the above example, everything is fine. Except, the stream will be suspended and cannot resume if any exception is thrown at runtime. Here is the reason why. As usual you will type:
without understanding how it works. By that way, you replace the current stream with a stream of the error object you handle. That's why the stream will be freezed after you handle the first error.
So here is a simple work around on how to handle the error in RxJS:
But using this way, we can not reset the state of the stream if we plan to use scan or throttle. So the way to deal with catchError without interfere the source stream is to lift the stream into nested stream using mergeMap:
However, the first case is still clean if we don't plan to store any state in the stream but still keep the stream flat.
For a conclusion, I will say that even error handler can be failed and the line of handling error or is quite blurry sometimes. So make sure that you added an error handler and not overthinking how to handle the error!
const errorHandler = catchError(err => {
// Reporting the error then emit it
return of(err)
})
return of(err)
const errorHandler = catchError((err, resetStream) => {
/**
* Reporting the error, then return the fallback value if there's any.
* Finally resume the stream.
*/
return of(fallbackValue).pipe(concat(resetStream))
})
source.pipe(
throttle(1000),
mergeMap(x =>
of(x).pipe(
map(x => divide(1, x)),
catchError(err => {
// Report the error and return the fallback value
return of(0)
})
)
)
)