Angular RxJS Error Handling

RxJS Error Handling – RxJS is a big part of Angular. Without a good understanding of how to do error handling the right way with RxJS, you are sure to run into an unexpected problems down the line when an error does occur. In contrast, if you know what you are doing up front, you can eliminate those unexpected problems and save yourself some debugging pain.

This RxJS article will examine:

  • The type of RxJS Observables to be most concerned about
  • RxJS Infinite Observables (see this article on the difference between finite and infinite
  • Observables – although you can probably guess)
  • How to incorrectly handle an error in RxJS
  • What happens when the error is incorrectly handled?
  • What happens when you don’t handle an error in RxJS?
  • How to correctly handle an error in RxJS
  •  

    The code for this article is available on github.

    Infinite Observables

    This article will be dealing with infinite observables – those that you expect to keep getting values from. That’s because if you do error handling wrong, they cease to be infinite observables and finish – which will be very bad since your application is expecting it to be infinite.

    These will be studied:

  • DOM Event – A DOM Event ‘keyup’ that you want to debounce as the user types on the page and then look up using an API
  •  

  • NgRx Effect – An NgRx Effect that you expect will always be listening for dispatched actions
  • DOM Event

    Case Study

    The first case study will focus on handling DOM events and doing searches based off of them.  There will be two input boxes that you type in a Star Wars character’s name.  When you stop typing for 300 milliseconds and the text is different than the last one, it will search for those names using the Star Wars API and display the results.  The first input box will continue to work after an error.  The second input box will stop working after an error.

    The Interface:

    I’ve gamed this a little bit so that if you type “error”, it will search with a wrong URL, thus creating an error.

    The relevant HTML:

    PGlucHV0IGNsYXNzPSJmb3JtLWNvbnRyb2wiIChrZXl1cCk9InNlYXJjaFRlcm0kLm5leHQoJGV2ZW50LnRhcmdldC52YWx1ZSkiIC8+Cgo8aW5wdXQgY2xhc3M9ImZvcm0tY29udHJvbCIgKGtleXVwKT0ic2VhcmNoVGVybUVycm9yJC5uZXh0KCRldmVudC50YXJnZXQudmFsdWUpIiAvPg==
    The keyup event simply emits using the “next” method of the Subject.

    The component code:

    c2VhcmNoVGVybSQgPSBuZXcgU3ViamVjdDxzdHJpbmc+KCk7CiAgICBzZWFyY2hUZXJtRXJyb3IkID0gbmV3IFN1YmplY3Q8c3RyaW5nPigpOwoKICAgIHRoaXMucnhqc1NlcnZpY2UKICAgICAgLnNlYXJjaEJhZENhdGNoKHRoaXMuc2VhcmNoVGVybUVycm9yJCkKICAgICAgLnBpcGUoCiAgICAgICAgZmluYWxpemUoKCkgPT4KICAgICAgICAgIGNvbnNvbGUubG9nKCJzZWFyY2hUZXJtRXJyb3IkIChiYWQgY2F0Y2gpIGZpbmFsaXplIGNhbGxlZCEiKQogICAgICAgICkKICAgICAgKQogICAgICAuc3Vic2NyaWJlKHJlc3VsdHMgPT4gewogICAgICAgIGNvbnNvbGUubG9nKCJHb3QgcmVzdWx0cyBmcm9tIHNlYXJjaCAoYmFkIGNhdGNoKSIpOwogICAgICAgIHRoaXMucmVzdWx0c0Vycm9yID0gcmVzdWx0cy5yZXN1bHRzOwogICAgICB9KTsKCiAgICB0aGlzLnJ4anNTZXJ2aWNlCiAgICAgIC5zZWFyY2godGhpcy5zZWFyY2hUZXJtJCkKICAgICAgLnBpcGUoZmluYWxpemUoKCkgPT4gY29uc29sZS5sb2coInNlYXJjaFRlcm0kIGZpbmFsaXplIGNhbGxlZCEiKSkpCiAgICAgIC5zdWJzY3JpYmUocmVzdWx0cyA9PiB7CiAgICAgICAgY29uc29sZS5sb2coIkdvdCByZXN1bHRzIGZyb20gc2VhcmNoIChnb29kIGNhdGNoKSIpOwogICAgICAgIHRoaXMucmVzdWx0cyA9IHJlc3VsdHMucmVzdWx0czsKICAgICAgfSk7
    This code basically will be reporting results to the page and logging whether it was called or not.  Note that we are calling two different service methods and passing in the two different Subjects.

    The error handling code for this case study is in the rxjsService:

    IHNlYXJjaEJhZENhdGNoKHRlcm1zOiBPYnNlcnZhYmxlPHN0cmluZz4pIHsKICAgIHJldHVybiB0ZXJtcy5waXBlKAogICAgICBkZWJvdW5jZVRpbWUoMzAwKSwKICAgICAgZGlzdGluY3RVbnRpbENoYW5nZWQoKSwKICAgICAgc3dpdGNoTWFwKHRlcm0gPT4gdGhpcy5zZWFyY2hTdGFyV2Fyc05hbWVzKHRlcm0pKSwKICAgICAgY2F0Y2hFcnJvcihlcnJvciA9PiB7CiAgICAgICAgY29uc29sZS5sb2coIkNhdWdodCBzZWFyY2ggZXJyb3IgdGhlIHdyb25nIHdheSEiKTsKICAgICAgICByZXR1cm4gb2YoeyByZXN1bHRzOiBudWxsIH0pOwogICAgICB9KQogICAgKTsKICB9CgogIHNlYXJjaCh0ZXJtczogT2JzZXJ2YWJsZTxzdHJpbmc+KSB7CiAgICByZXR1cm4gdGVybXMucGlwZSgKICAgICAgZGVib3VuY2VUaW1lKDMwMCksCiAgICAgIGRpc3RpbmN0VW50aWxDaGFuZ2VkKCksCiAgICAgIHN3aXRjaE1hcCh0ZXJtID0+CiAgICAgICAgdGhpcy5zZWFyY2hTdGFyV2Fyc05hbWVzKHRlcm0pLnBpcGUoCiAgICAgICAgICBjYXRjaEVycm9yKGVycm9yID0+IHsKICAgICAgICAgICAgY29uc29sZS5sb2coIkNhdWdodCBzZWFyY2ggZXJyb3IgdGhlIHJpZ2h0IHdheSEiKTsKICAgICAgICAgICAgcmV0dXJuIG9mKHsgcmVzdWx0czogbnVsbCB9KTsKICAgICAgICAgIH0pCiAgICAgICAgKQogICAgICApCiAgICApOwogIH0KCiAgcHJpdmF0ZSBzZWFyY2hTdGFyV2Fyc05hbWVzKHRlcm0pIHsKICAgIGxldCB1cmwgPSBgaHR0cHM6Ly9zd2FwaS5jby9hcGkvcGVvcGxlLz9zZWFyY2g9JHt0ZXJtfWA7CiAgICBpZiAodGVybSA9PT0gImVycm9yIikgewogICAgICB1cmwgPSBgaHR0cHM6Ly9zd2FwaS5jby9hcGl4L3Blb3BsZS8/c2VhcmNoPSR7dGVybX1gOwogICAgfQoKICAgIHJldHVybiB0aGlzLmh0dHAuZ2V0PGFueT4odXJsKTsKICB9

    Bad Error Handling

    SearchBadCatch

    The “searchBadCatch” method has our bad error handling code.  Just looking at it, it looks fine, right?  It is debouncing for 300 ms, has the distinctUntilChanged to make sure we don’t search for the same thing twice in a row.  There is a switchMap that calls the “searchStarWarsNames” method and we are catching errors using the catchError method.  What’s wrong with it?

    If you catch the error using “catchError” at the first level of the Observables “pipe” method (in this case return terms.pipe()), it will allow you to handle the error and return one more result in the stream but will then end the observable stream.  And that means it won’t listen to “keyup” events anymore.  Therefore, at all cost, never allow an error to percolate to this level.

    Note that if “catchError” is reached on the first level of the Observable “pipe” method, the “finalize” operator will be called.  You can see that up in the component code.

    Visual I hope will help:

    Never let an error percolate to the level of the red line.

    Good Error Handling

    “Search” Method

    The “search” method has our RxJS best practice error handling code:

    Always put the “catchError” operator inside a switchMap (or similar) so that it only ends the API call stream and then returns the stream to the switchMap, which continues the Observable. If you are not calling an API, make sure to add a try/catch block so that you can handle the error in the catch block and not allow it to percolate to the first level “pipe”.  Don’t assume your code will “never fail”, use a try/catch block.

    So you can see in the code that we add a “pipe” to the “searchStarWarsNames” call so that inside of there, we catchError and thus not allow the error to percolate to the first level “pipe”.

    Best practice visual:

    Always catch errors inside of the switchMap/mergeMap/concatMap, etc.

    Output

    Now it’s time to see how this works on the website.  We can assume that it works at first.  The fun begins when an error is generated from the API call.

    First, I’ll type “error” in both input boxes as shown:

    I’ll leave it as an exercise for you to see the console output.  Now for the real test, can I type in something and get results after handling the error?

    The first one works but the second one doesn’t anymore:

    The second input box is what I was talking about with a “unexpected problem” in the introduction. You would have a tough time figuring out why your search quit working.

    NgRx Effect

    Case Study
    The reason I started writing this article was that I had a “weird problem” with one of my applications that was using NgRx and Effects.  See here for information on effects.  Could it be that I wasn’t properly handling RxJS errors inside the effect?  As you’ll see in this study, the answer is “yes”.

    The Interface:

    Nothing fancy here:

    • “Success” – calls the Star Wars API with “person/1” (Luke Skywalker) and outputs the name on the screen
    • “Error – Stops Listening” – calls the API with a wrong URL so it generates an error – the catch is done wrong so it stops listening for the effect
    • “Error – Don’t catch error” – calls the API with a wrong URL so it generates an error – not catching the error
    • “Error – Keeps Listening” – calls the API with a wrong URL so it generates an error – properly catching the error so you can click it multiple times

    I’ll skip the HTML for this since it’s just buttons calling component methods.  Here is the component code:

    bmdyeFN1Y2Nlc3MoKSB7CiAgICB0aGlzLnN0b3JlLmRpc3BhdGNoKG5ldyBDYWxsV2l0aG91dEVycm9yKCkpOwogIH0KCiAgbmdyeEVycm9yKCkgewogICAgdGhpcy5zdG9yZS5kaXNwYXRjaChuZXcgQ2FsbFdpdGhFcnJvcigpKTsKICB9CgogIG5ncnhFcnJvcktlZXBMaXN0ZW5pbmcoKSB7CiAgICB0aGlzLnN0b3JlLmRpc3BhdGNoKG5ldyBDYWxsV2l0aEVycm9yS2VlcExpc3RlbmluZygpKTsKICB9CgogIG5ncnhFcnJvckRvbnRDYXRjaCgpIHsKICAgIHRoaXMuc3RvcmUuZGlzcGF0Y2gobmV3IENhbGxXaXRoRXJyb3JOb3RDYXVnaHQoKSk7CiAgfQ==
    The error handling good, bad and ugly is in the effect code.

    CallWithoutError Effect

    Success Case

    This is our success case:

    QEVmZmVjdCgpCiAgY2FsbFdpdGhvdXRFcnJvciQgPSB0aGlzLmFjdGlvbnMkLnBpcGUoCiAgICBvZlR5cGUoQXBwQWN0aW9uVHlwZXMuQ2FsbFdpdGhvdXRFcnJvciksCiAgICBzd2l0Y2hNYXAoKCkgPT4gewogICAgICBjb25zb2xlLmxvZygiQ2FsbGluZyBhcGkgd2l0aG91dCBlcnJvciIpOwoKICAgICAgcmV0dXJuIHRoaXMuaHR0cC5nZXQ8YW55PihgPGEgY2xhc3M9InZnbG5rIiBocmVmPSJodHRwczovL3N3YXBpLmNvL2FwaS9wZW9wbGUvMSIgcmVsPSJub2ZvbGxvdyI+PHNwYW4+aHR0cHM8L3NwYW4+PHNwYW4+Oi8vPC9zcGFuPjxzcGFuPnN3YXBpPC9zcGFuPjxzcGFuPi48L3NwYW4+PHNwYW4+Y288L3NwYW4+PHNwYW4+Lzwvc3Bhbj48c3Bhbj5hcGk8L3NwYW4+PHNwYW4+Lzwvc3Bhbj48c3Bhbj5wZW9wbGU8L3NwYW4+PHNwYW4+Lzwvc3Bhbj48c3Bhbj4xPC9zcGFuPjwvYT5gKS5waXBlKAogICAgICAgIG1hcChyZXN1bHRzID0+IHJlc3VsdHMubmFtZSksCiAgICAgICAgc3dpdGNoTWFwKG5hbWUgPT4gb2YobmV3IFNldE5hbWUobmFtZSkpKSwKICAgICAgICBjYXRjaEVycm9yKGVycm9yID0+IG9mKG5ldyBTZXROYW1lKCJFcnJvciEiKSkpCiAgICAgICk7CiAgICB9KSwKICAgIGZpbmFsaXplKCgpID0+IGNvbnNvbGUubG9nKCJDYWxsV2l0aG91dEVycm9yIGZpbmFsaXplIGNhbGxlZCEiKSkKICApOw==
    This one will work every time but if it failed, it would continue working because the “catchError” is inside the http.get “pipe”.  For the success case, the SetName reducer will add the “name” to the store.  The UI picks that up and displays it.

    CallWithError Effect

    Wrong URL
    This effect will call the API with the wrong URL so an error is generated.  The error handling is done incorrectly so once called, this will never work again until the app is refreshed.
    QEVmZmVjdCgpCiAgY2FsbFdpdGhFcnJvciQgPSB0aGlzLmFjdGlvbnMkLnBpcGUoCiAgICBvZlR5cGUoQXBwQWN0aW9uVHlwZXMuQ2FsbFdpdGhFcnJvciksCiAgICBzd2l0Y2hNYXAoKCkgPT4gewogICAgICBjb25zb2xlLmxvZygiQ2FsbGluZyBhcGkgd2l0aCBlcnJvciAtIHN0b3AgbGlzdGVuaW5nIik7CgogICAgICByZXR1cm4gdGhpcy5odHRwLmdldDxhbnk+KGA8YSBjbGFzcz0idmdsbmsiIGhyZWY9Imh0dHBzOi8vc3dhcGkuY28vYXBpeC9wZW9wbGUvMSIgcmVsPSJub2ZvbGxvdyI+PHNwYW4+aHR0cHM8L3NwYW4+PHNwYW4+Oi8vPC9zcGFuPjxzcGFuPnN3YXBpPC9zcGFuPjxzcGFuPi48L3NwYW4+PHNwYW4+Y288L3NwYW4+PHNwYW4+Lzwvc3Bhbj48c3Bhbj5hcGl4PC9zcGFuPjxzcGFuPi88L3NwYW4+PHNwYW4+cGVvcGxlPC9zcGFuPjxzcGFuPi88L3NwYW4+PHNwYW4+MTwvc3Bhbj48L2E+YCkucGlwZSgKICAgICAgICBtYXAocmVzdWx0cyA9PiByZXN1bHRzLm5hbWUpLAogICAgICAgIHN3aXRjaE1hcChuYW1lID0+IG9mKG5ldyBTZXROYW1lKG5hbWUpKSkKICAgICAgKTsKICAgIH0pLAogICAgY2F0Y2hFcnJvcihlcnJvciA9PiBvZihuZXcgU2V0TmFtZSgiRXJyb3IgLSBZb3UncmUgZG9vbWVkISIpKSksCiAgICBmaW5hbGl6ZSgoKSA9PiBjb25zb2xlLmxvZygiQ2FsbFdpdGhFcnJvciBmaW5hbGl6ZSBjYWxsZWQhIikpCiAgKTs=

    In this case, the “catchError” at the first level of the this.actions$.pipe will get called, thus ending the effect because its Observable stream will end.  This is just like in the case study above using just RxJS Observables.  We should see “Error – You’re doomed!” on the page after clicking it.  If we try clicking that button again, it will not fire the effect.

    The output for this:

    CallWithErrorKeep-Listening Effect

    Wrong URL Handled Properly
    This effect will call the API with the wrong URL so an error is generated.  However, it will handle the error properly so that it can be called again.
    QEVmZmVjdCgpCiAgY2FsbFdpdGhFcnJvcktlZXBMaXN0ZW5pbmckID0gdGhpcy5hY3Rpb25zJC5waXBlKAogICAgb2ZUeXBlKEFwcEFjdGlvblR5cGVzLkNhbGxXaXRoRXJyb3JLZWVwTGlzdGVuaW5nKSwKICAgIHN3aXRjaE1hcCgoKSA9PiB7CiAgICAgIGNvbnNvbGUubG9nKCJDYWxsaW5nIGFwaSB3aXRoIGVycm9yIC0ga2VlcCBsaXN0ZW5pbmciKTsKCiAgICAgIHJldHVybiB0aGlzLmh0dHAuZ2V0PGFueT4oYDxhIGNsYXNzPSJ2Z2xuayIgaHJlZj0iaHR0cHM6Ly9zd2FwaS5jby9hcGl4L3Blb3BsZS8xIiByZWw9Im5vZm9sbG93Ij48c3Bhbj5odHRwczwvc3Bhbj48c3Bhbj46Ly88L3NwYW4+PHNwYW4+c3dhcGk8L3NwYW4+PHNwYW4+Ljwvc3Bhbj48c3Bhbj5jbzwvc3Bhbj48c3Bhbj4vPC9zcGFuPjxzcGFuPmFwaXg8L3NwYW4+PHNwYW4+Lzwvc3Bhbj48c3Bhbj5wZW9wbGU8L3NwYW4+PHNwYW4+Lzwvc3Bhbj48c3Bhbj4xPC9zcGFuPjwvYT5gKS5waXBlKAogICAgICAgIG1hcChyZXN1bHRzID0+IHJlc3VsdHMubmFtZSksCiAgICAgICAgc3dpdGNoTWFwKG5hbWUgPT4gb2YobmV3IFNldE5hbWUobmFtZSkpKSwKICAgICAgICBjYXRjaEVycm9yKGVycm9yID0+IG9mKG5ldyBTZXROYW1lKCJFcnJvciBidXQgc3RpbGwgbGlzdGVuaW5nISIpKSkKICAgICAgKTsKICAgIH0pLAogICAgZmluYWxpemUoKCkgPT4gY29uc29sZS5sb2coIkNhbGxXaXRoRXJyb3JLZWVwTGlzdGVuaW5nIGZpbmFsaXplIGNhbGxlZCEiKSkKICApOw==

    The right way to handle the RxJS error is by putting the “catchError” inside the http.get “pipe”.  It will end the http.get observable but that doesn’t matter because it is a finite observable anyways and only emits one value.  When it returns the SetName action, the switchMap will emit it and continue the Observable stream.  Note that the finalize here will never be called.

    The output for this:

    CallWithError-NotCaught Effect

    When You Don’t Catch Error
    This is our last effect and answers our question of “What happens if we don’t catch the error?”  The answer to that question is that it behaves the same way as if we handled the error improperly (since that’s how RxJS rolls).  It’s just that you aren’t hooking into that error stream.
    QEVmZmVjdCgpCiAgY2FsbFdpdGhFcnJvckRvbnRDYXRjaCQgPSB0aGlzLmFjdGlvbnMkLnBpcGUoCiAgICBvZlR5cGUoQXBwQWN0aW9uVHlwZXMuQ2FsbFdpdGhFcnJvck5vdENhdWdodCksCiAgICBzd2l0Y2hNYXAoKCkgPT4gewogICAgICBjb25zb2xlLmxvZygiQ2FsbGluZyBhcGkgd2l0aCBlcnJvciAtIGRvbid0IGNhdGNoIik7CgogICAgICByZXR1cm4gdGhpcy5odHRwLmdldDxhbnk+KGA8YSBjbGFzcz0idmdsbmsiIGhyZWY9Imh0dHBzOi8vc3dhcGkuY28vYXBpeC9wZW9wbGUvMSIgcmVsPSJub2ZvbGxvdyI+PHNwYW4+aHR0cHM8L3NwYW4+PHNwYW4+Oi8vPC9zcGFuPjxzcGFuPnN3YXBpPC9zcGFuPjxzcGFuPi48L3NwYW4+PHNwYW4+Y288L3NwYW4+PHNwYW4+Lzwvc3Bhbj48c3Bhbj5hcGl4PC9zcGFuPjxzcGFuPi88L3NwYW4+PHNwYW4+cGVvcGxlPC9zcGFuPjxzcGFuPi88L3NwYW4+PHNwYW4+MTwvc3Bhbj48L2E+YCkucGlwZSgKICAgICAgICBtYXAocmVzdWx0cyA9PiByZXN1bHRzLm5hbWUpLAogICAgICAgIHN3aXRjaE1hcChuYW1lID0+IG9mKG5ldyBTZXROYW1lKG5hbWUpKSkKICAgICAgKTsKICAgIH0pLAogICAgZmluYWxpemUoKCkgPT4gY29uc29sZS5sb2coIkNhbGxXaXRoRXJyb3JOb3RDYXVnaHQgZmluYWxpemUgY2FsbGVkISIpKQogICk7
    Also, the name on the UI will not be set since you are not calling SetName in the “catchError” operator.  So you will either not see any output if it was the first button clicked or you’ll see the last name that was set.  Another “unexpected problem” that would be hard to debug.
    Conclusion

    Know How To Properly Handle RxJS Errors In Your Angular Application using Angular RxJS Error Handling

    Java is making strides toward modernity. For one thing, As you can tell from this article, knowing how to properly handle RxJS errors in your Angular application will help you to prevent those “weird problems” you could see when your infinite Observable stream ends unexpectedly.  Using this knowledge, you should be able to ensure that your infinite Observables never end until you decide they are finished.

    GET TO KNOW US

    Rich Franzmeier

    Consultant

    Rich Franzmeier is a senior developer at Intertech and author of several blog posts on topics ranging from Angular to NgRx to Javascript.

     

    “Whether you need help implementing Agile or need to boost your capabilities with a team of software developers that have proven expertise in design and development, no matter the technology, our proven consulting services can help you succeed the first time.”

     

    Consulting Services

    Consider introducing Intertech’s experience into your project and you will realize a higher degree of success around project goals, deliverables, timelines, and budgets.

    Training Services

    Consider Intertech for your companies training needs! A primary provider of training for fortune 5 to 5000 companies, Intertech brings a (1) proven resource that (2) knows how to plug into your system in a way that (3) accents your internal university system and makes you look great.

    Discover All That Intertech Can Do For You!

    Let’s Build Something Great Together!

    We’re ready to get started.