Thursday, February 5, 2015

Contemplating Code: My introduction to Rx and Reactive Programming.

I have the extreme pleasure of working with one of the best educators and proponents of Rx in the software industry.  When he first introduced our team to Rx, I was skeptical.  What could this reactive programming stuff really bring to the table.  But over time, I've really gotten hooked on writing Rx to solve problems.

In non-Rx programming, you might write a bit of code that looks like this to listen for events from a mouse.

function listenToMouseEvents(){
    mouseEventEmitter.addEventListener(mouse, function(event) {
        processMouseEvent(event);
    });
}

Oh, but you need a bit more than that, actually.  Because this will probably live inside a class of some sort.  And you need to maintain its lifecycle.  And you probably want to catch exceptions.

function MouseEventProcess (mouseEventEmitter) {
    this._mouseEventEmitter = mouseEventEmitter;
    this._boundOnMouseEvent = this._onMouseEvent.bind(this);

    mouseEventEmitter.addEventListener('mouse', this._boundOnMouseEvent);
};

MouseEventProcessor.prototype = {
    _onMouseEvent: function(event) {
        try {
            this._processMouseEvent(event);
        } catch(e) {
            this._processException(e);
        }
    },

    destroy: function() {
        this._mouseEventEmitter.
            removeEventListener('mouse', this._boundOnMouseEvent);
    }
};

You might start building functionality around the mouse where you support various kinds of gestures, and have a library for emitting those.  But you need to be able to manage all those life cycles simultaneously.  Even the example above misses issues where errors cause us to want to remove our event listener.

Using Rx, you get that for free, and don't have to do much different.  Heck, I think the following example looks even simpler than before.

function MouseEventProcess (mouseEventEmitter) {
    this._compositeDisposable = new Rx.Observable.CompositeDisposable();

    var disposable = Rx.Observable.fromEvent(mouseEventEmitter, 'mouse').
         .subscribe(
             this._processMouseEvent.bind(this),
             this._processException.bind(this),
             NOOP);

    this._compositeDisposable.add(disposable);
}

MouseEventProcess.prototype.destroy = function() {
    this._compositeDisposable.dispose();
}

This is a simplistic example of how Rx gets started.  Where Rx really shines is when you start adding more complexity to it.  By creating multiple gestures, etc., you just keep chaining off of the initial mouse event emitter.  By adding everything to the composite disposable, you get to clean it all up when you're done.  There's also other tricks that are suddenly surprisingly easy, and tons of "free" stuff you get when you do things this way.

Most importantly, you don't have to spend as much time managing the lifecycle of your event emitters, callbacks, and listeners.  By just disposing of the disposable returned by the subscribe method, every observable subscription in the observable chain is automatically unsubscribed, and that does all the work you need it to do.

Building bullet proof applications in Rx seems a lot easier once you get to know it, but hoo boy is it a bit of a steep learning curve.  Particularly if you don't know anything about functional programming.

Next time, I should talk about the difference between reactive programming and procedural programming as I understand it so far, and go into detail about the benefits that Rx provides me.

No comments:

Post a Comment