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.
Reader Comments