Ditching old school promises in favor of RxJs Observables

This post will be continued to be updated as I have more time and should be considered a work in progress so subscribe to this event stream yo ;)

The Preamble

You may be familiar with observables. You may have used them on a cursory level, or you may be completely new to them. Regardless of your current standing, my hope is that you might pick up something you didn't know before or have a lightbulb moment and maybe think about something a different way.

The aim of this article is to give a concise and in depth understanding of what observables are and how they work so that by the end of this you will be able to explain them yourself in layman's terms.

The Credit

Up until recently, my understanding of observables was also on a more limited level. I had used them in Angular 2, kind of knowing what they were doing, but looking at them like they were a bit of black magic. I credit the improvement to my understanding primarily to the wonderful foundation I got from the guys at thoughtram while attending Angular 2 Master Class and secondarily from talks from people like Ben Lesh. Enough preamble, let's dig in!

Promise Recap

Promises are a way to handle asyncrhonous events. They say, when this happens, then we will do that. They have become the defacto way to handle async events for many years. Promises have a few characteristics that you should know about:

  • Promises will have one of three states: pending, fulfilled, or rejected.
  • They are a one time thing: once a promise resolves, it cannot be changed to a different state or reused, any time you reuse it afterwards it will immediately resolve since it has already finished.
  • Once a promise is initiated, even if you delete the promise, the resulting action is still ensuing.

Once you get the result of a promise, wether it's an ajax call or something else you then have to do something with it. Map it, mutate it, or something to that effect.

So what is the problem with promises?

The problem is, many applications today need more than one off async events. They handle things like UI interaction, button clicks and such, video streaming, and more complicated tasks. Once these async events come back there are often transformations and things that must take place.

Observables and RxJs provide a reactive way to handle asyc events and they efficiently handle the values returned from them.

Enter ReactiveX

"ReactiveX is a library for composing asynchronous event based programs.", according to it's website. It is based on the observer pattern, which is a pattern with concepts of things that listen for events, and other things which react to those events. Hence the name, ReactiveX. To me, conceptually speaking, the easiest way to understand what an observable is is to think of it as the observer pattern with some underscore / lodash-like operators mixed in.

So what's an Observable

Think of observables as promises with special powers. As a jQuery wrapper gives a dom object special abilities, observable are like promises with special abilities ( but a lot more than that ). For now, this is an easier way to think of them. We are going to expound further on this definition but for now, this sets our mental stage for more. Before we get going with things, let's look at the kind of behavior you will be learning to implement.

Now that you have a vague, yet intriguing idea, of what observables are, let's go over a few terms that you need to know about observables.

Each of these will be explored more after we define them

Event stream
This is the thing that probably tripped me up most when I began learning observables. When hearing the term stream, I was thinking of internet video protocols that sent chunks of 0's and 1's across the wire streaming video and music and such. In reality though, an event stream is just a collection of 1 or more events. Think of it like an array. That array can have 50 items in it, or it can have 1 item in it. Either way it's still an array. An event stream, can have 1 event, or many events.

When most people think of events they think of things like a click, so let's use that as the centerpiece of this part of our discsussion. When you do something like this:

$('.someButton').on('click', myClickHandler);

You have a source, which is .someButton and you have a subscriber myClickHandler. When the button get's clicked, the handler will immediately fire. That's not a stream of events. That is just one event that fires and then it's done. You can't really work with it further. Take a look at the same concept as on observable:

var input  = Rx.Observable.fromEvent(document.querySelector('.someButton'), 'click');

input.delay(200).subscribe(evt => {  
  //Call event handler here
  console.log(evt);
});

While it's slightly more code, the differences and extra power you get from it is pretty astounding.
Essentially, what the code above does is it creates an observable that reacts to events, as opposed to just a 1 and done event handler. This code above is basically a RxJs version of an event handler. Rx's event handlers do much more than standard event handlers though. They let preprocess events before they are handled, control the flow of events, unsubscribe to events, and compose event streams.

A Key concept to remember is observable operators process events before they are sent to RxJs's version of a handler.

So what's the deal with the delay in the code above? As we glossed over above, not only do observables allow you to maintain the state of events, but they also allow you to control event flow. Delay is one of Rx's flow control operators. It allows you to delay the handling of the event for X time. This is just one example of RxJs's many flow control operators.

Restated another way, event streams are an indeterminate amount of events that can happen at any time. If that's not any less cryptic, you can think of user clicks that can happen on a page at any time over the course of a time period, or a fly that can land on your drink over and over, randomly, whenever it wants while you have lunch for a few hours.

Event streams are any number of X events that happen over a period of Y time.

A more real world example of an event stream is like the tortured task that every man must endure: Shopping at the store with a significant other.

While the man stands by the cart, trying to participate, yet tortuously enduring on their smart phone, they observe items being pushed into their shopping cart at any time over what seems like an eternity.

This pattern describes the magic formula to understand an event stream's basic anatomy: you observe an event that happens over a period of time.

You can see an example of that here in the top diagram. Enough about event streams, onto the next term for your observable vocabulary words.

Observable: Something that can be watched.
It can notify other things that are watching it that something has happened. So you can say it can send notifications or aka emit events.

Can send values. Can be subscribed to.

Going back to our jQuery example:

$('.someButton').on('click', myClickHandler);

Here we are observing $(.someButton). It is the thing that is being watched. With Rx though, you don't just get a jQuery object. You get something that can handle a stream of ongoing events, something that can have operators called on its' result before it invokes the handler logic. You get a much more robust and scalable API.

Observer: Something that does the watching
An observer registers an interest in an observable by subscribing. You can think of this as a listener.

Subscribes, value it recieves can be transformed with operators like map, filter, ect..

Subject: Something that can both watch and be watched.

Now that you have seen a few concepts let's look at a bit more of a hands on observable example:

//#1. Create an obserable source - a broadcaster
var source = Rx.Observable.create(observer => {  
  observer.next('Bam!');
  observer.complete();

  return () => console.log('subscription canceled')  //Teardown function
});

//#2. Create a listener / subscriber.
var subscription = source.subscribe(  
  value => console.log('next: ' + value),
  err => console.log('error: ' + err),
  () => console.log('completed')
);

subscription.unsubscribe();      //cancels the subscription  

What is happening above? When you create an observable from scratch using this technique, it produces an observable. You can then act on that observable in the callback. Whatever is returned from the observable will be invoked as the teardown method when you dispose of the subscription.

Next we subscribe to the observable calling source.subscribe. This tells the observable to start emitting events or sending out notifications if you prefer that terminology. The subscription receives values that are pushed to it from the observable.

When you subscribe to an observable source, RxJs returns what it calls a disposable which allows you to manage the subscription state. You can do things like call .unsubscribe on the object.

Source: The thing that's emitting your observable stream.

Subscription: The thing that's listening for an event stream and then reacting to the values that are pushed to it.

Laziness: You might hear it said that observers are lazy by nature. This just means that they don't do anything until you tell them you are listening to them. Sounds like an odd concept but it's rather easy to grasp if you visualize an example.

Think of an elaborate arrangement of fireworks for fourth of July. You set them up in patterns, aim them in different directions, filter out the dud's that look damaged but nothing happens until you light the fuse. You light an observable's fuse when you call it's .subscribe() method.

Internal vs External events

In RxJs there are two broad species of events: Internal and external.
With internal events, you create an observer manually or via one of the operators, and then react to events over time, wrapping the source in an observable. An example of this would be a websocket, video stream, or maybe wrapping an XHR request.

With external events, you can trigger events from anywhere, think event bus. More on this in a bit.

More on the Observable / Observer relationship

A key concept that is worth repeating, observer / observable relationship is that observers subscribe to observables. Similar to an event bus, you have something that listens for events to take place and something that emit's those events.

Furthermore, in other event models, the system polls for event, actively participating in the listening process. This can be draining from a performance perspective. The Observable event model has an inverse relationship though, the client code pushes events to the listener and the listener takes a passive role in the observation process. This is the reason that a well planned reactive event approach can give your application a little performance bump over traditional event models and make it feel a bit more snappy.

Observers listen for events from observables.

Observer iteration behavior
Another thing that makes observables superior to standard event handling is the way they handle iteration over values.

In a standard world, let's imagine a button click that results in the production of a large array of values. Typically, if you have to do some work on those values, iterating over each item in the array, you have a bottleneck because the transformed values wont be available until the whole array has finished being iterated over.

This is not so with observables, with observables, each as each next tick happens, it pushes through the value to the code that handles it. According to ReactiveX, this makes working with large data sets faster using observables, but if the array is small then standard iteration methods would indeed be faster.

Tip: Most of the time when you want to try a JS project out you can go to their site and do it in the console! That is exactly the case here. Head over to RxJS's api docs and you can play around w this stuff as you read it.

Creating these bad hombres

So you have seen above a few of the ways you can create observables. Observables can be created using a broad group of mechanisms and strategies. This is too broad of a subject to go in depth in one article, besides, it would most likely put you to sleep, but we'll touch on a few.

Some of the ways that you can create observables are:

  • From events as you saw above.
  • Manually created, also, as you saw above
  • Created via Subjects / BehaviorSubjects
  • Creation via operators
  • From an array
  • From a callback
  • From a promise
  • And still there are more ways...

Subjects

Subjects are a special type of observables that act as both observers and observables. In other words they are broadcasters and listeners at the same time. In addition, normal observables are unicast, each subscriber get's it own instance of an observable. Subjects however are multicast, they can broadcast to many subscribers at the same time. Let's check out an example of how these work:

var subject = new Rx.Subject();

subject.subscribe({  
  next: (val) => console.log('observer a: ' + val)
});

subject.subscribe({  
  next: (val) => console.log('observer b: ' + val)
});

subject.subscribe({  
  next: (val) => console.log('observer c: ' + val)
});

subject.next(1);  
subject.next(2);  
subject.next(3);  

* Example courtesy of the RxJs docs.

So let's unpack what's going on here. You have one subject acting as the source and three observers of it. Note how you can use the subject to setup both sides of the equation, broadcaster and listener, with little headache.

One thing to keep in mind though, if you want access to the current value with subjects or observables, you are strait out of luck. When a value is emitted, these guys MO is to pass the value through to the subscribers and their job is finished. If your solution requires a bit more transparency with the current value that is being emitted then you should take a look at behavior subjects.

Behavior Subjects are a special subset of subjects. They have all of the same subject powers but with a few extra benefits. Most notably, BehaviorSubject's (BH's) are designed to expose the current value. When you create an instance of a BH you have to give it an initial value, even if that value is null. If in the example above, you just change new Subject to new BehaviorSubject(null); you will be able to get the current value of the subject whenever you want like so: subject.getValue().

Creation Strategies

Most of the time you will most likely be creating observables via operators or through subjects. It will be more of a rarity than the norm that you actually create and implement both sides of the observer / observable relationship from scratch.

Generally speaking, you will probably be using Rx.Observable.fromEvent or Rx.Observable.from(someArray) unless you have an awesome framework like Angular 2 that has observables baked in. Basically some other strategy to create you observables. So if there are so many strategies that abstract away the tediousness of manual implementation, why would you want to create an observable manually with the create operator?

The answer is, if you want to create an observable event out of something that doesn't really have an observable API. A perfect example of this is how Angular 2 implements it's Http module. The meat of the observable part is in a class called XHRConnection and it can be found here: @angular/http/src/backends/xhr_backend.ts

So this class is pretty big so i'm going to take a stab at omitting some stuff just for the sake of focusing on how it relates to observables, but you should definately take a look at it. It's pretty cool.

export class XHRConnection implements Connection {  
  request: Request;
  /**
   * Response {@link EventEmitter} which emits a single {@link Response} value on load event of
   * `XMLHttpRequest`.
   */
  response: Observable<Response>;
  readyState: ReadyState;
  constructor(req: Request, browserXHR: BrowserXhr, baseResponseOptions?: ResponseOptions) {
    this.request = req;
    this.response = new Observable<Response>((responseObserver: Observer<Response>) => {
        //More XHR code ^^^ ......

        //Success Handler
        const response = new Response(responseOptions);
        response.ok = isSuccess(status);
        if (response.ok) {
          responseObserver.next(response);
          responseObserver.complete();
          return;
        }
        responseObserver.error(response);
      };

      //Error event handler
      const onError = (err: ErrorEvent) => {
        let responseOptions = new ResponseOptions({
          body: err,
          type: ResponseType.Error,
          status: _xhr.status,
          statusText: _xhr.statusText,
        });
        if (baseResponseOptions != null) {
          responseOptions = baseResponseOptions.merge(responseOptions);
        }
        responseObserver.error(new Response(responseOptions));
      };
  }
}

So when you get down on a low level, this is the abstraction that angular's http method handles for you. The important things to note here are how the observable handles the success handler and the on error handler.

Conclusion

If I missed anything or got anything wrong please feel free to let me know in the comments. In addition, if the information in this article was helpful to you at all, you should thank the guys at Thoughtram and consider taking their Angular 2 Master Class. It gave me a better understanding of observables and a great starting point to dive deeper into my own studies.

Resources