Skip to main content
Ben Nadel at the jQuery Conference 2010 (Boston, MA) with: John Hann
Ben Nadel at the jQuery Conference 2010 (Boston, MA) with: John Hann ( @johnmhann )

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

By 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.

Want to use code from this post? Check out the license.

Reader Comments

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel