Converting A Subject To An Observable Using RxJS In Angular 2

In Angular 2, you can use the Rx.Subject class to create the source of an observable sequence. But, you never want to return an Rx.Subject instance to the calling context. Doing so would be somewhat akin to returning a Deferred object rather than a promise; and, it would leave the Subject open to unanticipated and corrupting usage. As such, when exposing a Subject, you'll probably want to convert it to an Observable first.

Run this demo in my JavaScript Demos project on GitHub.

Up until now, as a newcomer to RxJS, I've been trying to do this - converting a Subject to an Observable - using the Rx.Observable.from() creation method. Unfortunately, while it appeared as if it was working (in the way that I intended), it actually was not. To demonstrate, we're going to use Rx.Observable.from() to try and convert a Subject to an Observable and then test whether or not we can call the .next() method on the result (if all goes well, we shouldn't be able to):

<!doctype html> <html> <head> <meta charset="utf-8" /> <title> Converting A Subject To An Observable Using RxJS In Angular 2 Beta 9 </title> </head> <body> <h1> Converting A Subject To An Observable Using RxJS In Angular 2 Beta 9 </h1> <h2> Using Rx.Observable.from() </h2> <!-- Load demo scripts. --> <script type="text/javascript" src="../../vendor/angularjs-2-beta/9/es6-shim.min.js"></script> <script type="text/javascript" src="../../vendor/angularjs-2-beta/9/Rx.umd.min.js"></script> <script type="text/javascript"> // I return a "hot" observable sequence that emits a value every 1,000 ms. function getObservable() { var source = new Rx.Subject(); setInterval( function emitNextValue() { source.next( new Date().getTime() ); }, 1000 ); // In an attempt to protect the "Subject" from the calling context (such that // the calling context cannot corrupt the Subject by explicitly invoking the // .next(), .complete(), or error() methods), we are going to try to create // an Observable from the Subject. // -- // CAUTION: This DOES NOT WORK - the Subject is passed-through because it is // technically already an Observable. return( Rx.Observable.from( source ) ); } // --------------------------------------------------------------------------- // // --------------------------------------------------------------------------- // var stream = getObservable(); // Subscribe to the observable sequence so we can debug the values. stream .take( 3 ) .subscribe( function handleValue( value ) { console.log( "Observable value:", value ); } ) ; // The value returned from getObservable() is supposed to be an Observable, // which means that we SHOULD NOT BE ABLE to call the .next() method on it. // Let's test it out! try { stream.next( "foo" ); // Expecting this to throw an undefined method error. console.warn( "Not good! You were able to call .next() on the stream." ); } catch ( error ) { console.info( "Thank goodness! You weren't able to call .next() on the stream." ); } </script> </body> </html>

As you can see, the getObservable() method is passing the internal Subject reference through the Rx.Observable.from() method. However, when we run this code, we get the following output:

As you can see, we were able to invoke the .next() method on the returned value which means that we accidentally returned the Subject back to the calling context. In essence, the Rx.Observable.from() method didn't do anything. And, in fact, if you look at the RxJS source code, you will see that this method will simply pass-through the given object if it is already an instance of the Observable class (which, of course, Subject is by way of inheritance).

The reason that I thought this was working was because the resultant value acts like an Observable. But, that's only because Subject is already an Observable.

So, basically, I've been wrong up until now (and will try to go back and correct some code).

To get this working in the way that we actually intended it to, we can use the Rx.Observable.prototype.asObservable() instance method. I actually saw this method a while back, but the description didn't make sense to me at the time:

Hides the identity of an observable sequence.

Why would I ever want to "hide" the identity of an observable? Seems like such an odd gesture. Until you remember that other classes can extend Observable. Then, it starts to make a little bit more sense. Though, I might rephrase it to be something like:

(Ben's version) Casts any object that implements the observable interface into a new Observable instance.

When you think about it that way, it's exactly what we want - to cast Subject (which implements Observable by way of inheritance) to Observable. So, let's give it a try:

<!doctype html> <html> <head> <meta charset="utf-8" /> <title> Converting A Subject To An Observable Using RxJS In Angular 2 Beta 9 </title> </head> <body> <h1> Converting A Subject To An Observable Using RxJS In Angular 2 Beta 9 </h1> <h2> Using Rx.Observable.prototype.asObservable() </h2> <!-- Load demo scripts. --> <script type="text/javascript" src="../../vendor/angularjs-2-beta/9/es6-shim.min.js"></script> <script type="text/javascript" src="../../vendor/angularjs-2-beta/9/Rx.umd.min.js"></script> <script type="text/javascript"> // I return a "hot" observable sequence that emits a value every 1,000 ms. function getObservable() { var source = new Rx.Subject(); setInterval( function emitNextValue() { source.next( new Date().getTime() ); }, 1000 ); // In an attempt to protect the "Subject" from the calling context (such that // the calling context cannot corrupt the Subject by explicitly invoking the // .next(), .complete(), or error() methods), we are going to try to create // an Observable from the Subject. return( source.asObservable() ); } // --------------------------------------------------------------------------- // // --------------------------------------------------------------------------- // var stream = getObservable(); // Subscribe to the observable sequence so we can debug the values. stream .take( 3 ) .subscribe( function handleValue( value ) { console.log( "Observable value:", value ); } ) ; // The value returned from getObservable() is supposed to be an Observable, // which means that we SHOULD NOT BE ABLE to call the .next() method on it. // Let's test it out! try { stream.next( "foo" ); // Expecting this to throw an undefined method error. console.warn( "Not good! You were able to call .next() on the stream." ); } catch ( error ) { console.info( "Thank goodness! You weren't able to call .next() on the stream." ); } </script> </body> </html>

Now, when we run the above code, we get the following output:

As you can see, when we tried to call .next() on the returned value, an error was thrown. This is because we successfully converted the Subject instance to an Observable instance, shielding the calling context from the Subject implementation.

While you can technically pass around instances of Subject, doing so allows implementation details to bleed into other parts of the application. To prevent this, it is best to convert Subjects to Observables so that the sequence is exposed in a read-only fashion. Luckily, this is quite easy to accomplish with the .asObservable() instance method inherited by the Subject class in RxJS.

Tweet This Groovy post by @BenNadel - Converting A Subject To An Observable Using RxJS In Angular 2 Woot woot — you rock the party that rocks the body!







