Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at NCDevCon 2011 (Raleigh, NC) with: Vicky Ryder
Ben Nadel at NCDevCon 2011 (Raleigh, NC) with: Vicky Ryder@fuzie )

Unhandled Errors In RxJS Observable Streams Will Throw Errors In Angular 2 Beta 6

By Ben Nadel on

One of the most often cited problems with Promises is that if you don't explicitly handle an error condition in a Promise chain, it's easy to accidentally swallow said error, hiding it from both process and logging. RxJS Observable streams, on the other hand, seem to take a completely different approach. In an RxJS Observable stream, if you don't handle an error condition explicitly, RxJS will handle it implicitly by throwing your error. In Angular 2 Beta 6, the RxJS stream is being managed (so to speak) by the core Zone.js instance which means that these unhandled errors are subsequently caught by the Angular 2 default ExceptionHandler service.


 
 
 

 
 
 
 
 

Run this demo in my JavaScript Demos project on GitHub.

To explore this RxJS feature, I put together a tiny little demo that does nothing but create an "error stream" without an error handler (ie, a subscriber that doesn't listen for error events). I also added a few error handling and error conversion functions that you can see in the video:

  • <!doctype html>
  • <html>
  • <head>
  • <meta charset="utf-8" />
  •  
  • <title>
  • Unhandled Errors In RxJS Observable Streams Will Throw Errors In Angular 2 Beta 6
  • </title>
  •  
  • <link rel="stylesheet" type="text/css" href="./demo.css"></link>
  • </head>
  • <body>
  •  
  • <h1>
  • Unhandled Errors In RxJS Observable Streams Will Throw Errors In Angular 2 Beta 6
  • </h1>
  •  
  • <my-app>
  • Loading...
  • </my-app>
  •  
  • <!-- Load demo scripts. -->
  • <script type="text/javascript" src="../../vendor/angularjs-2-beta/6/es6-shim.min.js"></script>
  • <script type="text/javascript" src="../../vendor/angularjs-2-beta/6/Rx.umd.min.js"></script>
  • <script type="text/javascript" src="../../vendor/angularjs-2-beta/6/angular2-polyfills.min.js"></script>
  • <script type="text/javascript" src="../../vendor/angularjs-2-beta/6/angular2-all.umd.js"></script>
  • <!-- AlmondJS - minimal implementation of RequireJS. -->
  • <script type="text/javascript" src="../../vendor/angularjs-2-beta/6/almond.js"></script>
  • <script type="text/javascript">
  •  
  • // Defer bootstrapping until all of the components have been declared.
  • // --
  • // NOTE: Not all components have to be required here since they will be
  • // implicitly required by other components.
  • requirejs(
  • [ /* Using require() for better readability. */ ],
  • function run() {
  •  
  • var App = require( "App" );
  •  
  • ng.platform.browser.bootstrap( App );
  •  
  • }
  • );
  •  
  •  
  • // --------------------------------------------------------------------------- //
  • // --------------------------------------------------------------------------- //
  •  
  •  
  • // I provide the root App component.
  • define(
  • "App",
  • function registerApp() {
  •  
  • // Configure the App component definition.
  • ng.core
  • .Component({
  • selector: "my-app",
  • template:
  • `
  • <p>
  • <a (click)="createErrorStream()">Create error stream</a>.
  • </p>
  • `
  • })
  • .Class({
  • constructor: AppController
  • })
  • ;
  •  
  • return( AppController );
  •  
  •  
  • // I control the App component.
  • function AppController() {
  •  
  • var vm = this;
  •  
  • // Expose the public methods.
  • vm.createErrorStream = createErrorStream;
  •  
  •  
  • // ---
  • // PUBLIC METHODS.
  • // ---
  •  
  •  
  • // I create an RxJS Observable error stream.
  • function createErrorStream() {
  •  
  • Rx.Observable
  • .throw( new Error( "Oops: I Did It Again." ) ) // I played with your heart, got lost in the game.
  • .subscribe(
  • function handleValue( value ) {
  •  
  • console.log( "handleValue:", value );
  •  
  • }
  •  
  • // NOTE: Uncomment this to handle the upstream error
  • // and prevent the error from being thrown.
  • // --
  • // , handleError
  •  
  • // Notice that we are not providing a CATCH handler in
  • // our subscriber configuration. As such, the underlying
  • // Subscriber class is automatically providing one for
  • // us, which basically implements:
  • // --
  • // function handleError( e ) { throw( e ); }
  • )
  • ;
  •  
  •  
  • // I log errors.
  • function handleError( error ) {
  •  
  • console.warn( "Caught an error in the stream." );
  • console.log( error );
  •  
  • }
  •  
  • }
  •  
  • }
  •  
  • }
  • );
  •  
  • </script>
  •  
  • </body>
  • </html>

As you can see, we're using the .throw() operator to start an error stream; but, we're not catching or handling said error. So, when we run this code and click the link, we get the following console output:


 
 
 

 
 Unhandled errors in an RxJS stream are rethrown in Angular 2 Beta 6. 
 
 
 

As you can see, the unhandled error was rethrown (for lack of a better term) by RxJS. This thrown error was then caught by Zone.js and piped back into the Angular 2 context where it was subsequently logged by the ExceptionHandler service.

Coming from a Promise background, this behavior kind of caught me by surprise. But, I think this is a really nice feature of RxJS Observable streams. It means that unexpected error conditions will never get lost in an Angular 2 application. Well, not if you're using streams.




Reader Comments

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.