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

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:

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.

Tweet This Fascinating post by @BenNadel - Unhandled Errors In RxJS Observable Streams Will Throw Errors In Angular 2 Beta 6 Woot woot — you rock the party that rocks the body!







