Skip to main content
Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

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