Introduction to the Reactive Extensions for JavaScript – Error Handling Part II

We’ve covered a bit recently with conditional and looping operators on the Reactive Extensions for JavaScript, but I want to step back just a minute and cover exception handling.  This post will cover how we can compensate for errors as they happen in several ways and will largely follow Bart de Smet’s post on the same topic, but instead of covering the Interactive Extensions, we’ll stick primarily in JavaScript.  Last time, we covered the basics of error handling including Catch and Finally.

Before we get started, let’s get caught up to where we are today:

OnErrorResumeNext VB Style

The next error handling feature in the Reactive Extensions for JavaScript is one that should be familiar with those in the Visual Basic world, called OnErrorResumeNext (On Error Resume Next).   In Visual Basic parlance, this meant you specify that when a run-time error occurs, control goes to the statement immediately following the statement where the error occurred, and execution continues from that point.  The functionality is similar to the Concat operator which allows you append Observable sequences onto each other to create a single sequence, except that should an error occur, it is not bubbled up and instead continues to the next.  This operator comes in two flavors, one as a static method and one as an instance method:

// items : Observable[]
// scheduler : optional custom scheduler
// returns : Observable

Rx.Observable.prototype.OnErrorResumeNext(
    items,
    scheduler);

// items : Observable[]
// returns : Observable
Rx.Observable.OnErrorResumeNext(
    items);

Let’s create a simple example where we want to output the numbers 1 through 9, even if an exception occurs along the way.  In this case, we yield two arrays and the third then has an exception concatenated to the end before then yielding the final array.

Rx.Observable.OnErrorResumeNext([
    Rx.Observable.FromArray([1, 2]),
    Rx.Observable.FromArray([3, 4, 5]),
    Rx.Observable.FromArray([6, 7]).Concat(Rx.Observable.Throw("woops!")),
    Rx.Observable.FromArray([8, 9])])
    .Subscribe(
        function (next) {
            $("<p/>").html(next).appendTo("#results");
        });

This is great for handling data that may be potentially bad and is ok to drop the exception should it occur.

Cleaning Up With Using

Just as in .NET there is the using keyword to denote a try/finally with a call to Dispose, we need a similar capability in the JavaScript world for resource cleanup in the asynchronous world.  Let’s look at the signature of the function which takes a resource selector which returns the Disposable object, and then the usage which takes the Disposable object and you return an Observable sequence.

// resourceSelector : () -> Disposable,
// resourceUsage : Disposable -> Observable
// returns : Observable

Rx.Observable.Using = function(
    resourceSelector, 
    resourceUsage);

A Disposable object is nothing more than an object with a Dispose method which takes no arguments and returns nothing.  For example, we could create a Disposable object to encompass the jQuery data API which allows us to store arbitrary data associated with a given element.  This object would allow us to both get and set the value, and in our Dispose, we would remove the data.

DataDisposable = function (element, key) {

    this.data = function (value) {
        return element.data(key, value);
    };

    // Dispose method
    this.Dispose = function () {
        element.removeData(key);
    };
};

Using this, we can write two samples, the first of which sets the data and then retrieves the data without an exceptional condition whereas the second example does.  let’s take a look at the first example which yields to us the answer of 56088.

Rx.Observable.Using(

    // resourceSelector
    function () {
        return new DataDisposable($("#content"), "key1");
    },
    
    // resourceUsage
    function (disposable) {
        disposable.data(123);
        
        return Rx.Observable.Return(456 * disposable.data());
    })
    .Subscribe(
        function (next) {
            $("<p/>").html(next).appendTo("#content");
        });

The second example below is the exact same, except that we throw an exception and then resume the next sequence which returns 789 instead of the answer above.

Rx.Observable.Using(

    // resourceSelector
    function () {
        return new DataDisposable($("#content"), "key1");
    },
    
    // resourceUsage
    function (disposable) {
        disposable.data(123);

        return Rx.Observable.Return(456 * disposable.data())
            .Concat(Rx.Observable.Throw("woops"));
    })
    .Catch(Rx.Observable.Return(789))
    .Subscribe(
        function (next) {
            $("<p/>").html(next).appendTo("#content");
        });

In both cases here, we ensure that the underlying data is properly disposed when we leave the scope of our Using operator.

If At First You Fail, Retry

Let’s move onto the last operator for this section on error handling, Retry.  The idea behind this operator is that we can retry the observable sequence until it succeeds (meaning no exception) or hits the maximum try count.  Let’s look at the method signatures below:

// returns : Observable

Rx.Observable.prototype.Retry();

// count : int
// scheduler : optional scheduler
// returns : Observable

Rx.Observable.prototype.Retry(
    count,
    scheduler);

Now if we have a sequence that doesn’t throw an exception, then it should be a standard no-op, which means it does nothing for example, the following simply prints the numbers 1 through 3.

Rx.Observable.FromArray([1, 2, 3])
    .Retry()
    .Subscribe(
        function (next) {
            $("<p/>").html(next).appendTo("#results");
        });

However, if we indeed had a collection that did throw an exception each and every time, we could run into a big issue such as the following, where it would blow out the JavaScript stack as it would try to go on forever:

Rx.Observable.FromArray([1, 2, 3])
    .Concat(Rx.Observable.Throw("woah nellie!"))
    .Retry()
    .Subscribe(
        function (next) {
            $("<p/>").html(next).appendTo("#results");
        });

Luckily, we have ways around this where we can specify the maximum number of retries that are allowed with the provided overload.  Next, let’s show a case where Retry is really used.  For example, let’s say we have a loop which could produce three values, but will fail at different points for the first two iterations. 

var count = 0;

Rx.Observable.Return(4)
    .Concat(
        Rx.Observable.If(
            function() { return count == 0; },
            Rx.Observable.Throw("nope!"),
            Rx.Observable.Return(5)))
    .Concat(
        Rx.Observable.If(
            function () { return count == 1; },
            Rx.Observable.Throw("nope!"),
            Rx.Observable.Return(6)))
    .Finally(function() { count++; })
    .Retry()
    .Subscribe(
        function (next) {
            $("<p/>").html(next).appendTo("#results");
        });

In this situation, we will render the following result, since we hit two different exceptions:

4 // Hits exception
4
5 // Hits exception
4
5
6

And there you have it, we have composable ways around potential failures in our asynchronous push based operations that give us the flexibility that our synchronous imperative ones do.

Conclusion

Dealing with asynchronous programming has been in the forefront of many minds in the JavaScript community.  At JSConf, there were several examples of frameworks trying to get around the idea of callbacks and instead lean more towards composition.  By utilizing the Reactive Extensions for JavaScript, we’re able to compose together asynchronous and event-based operations together and transform the results in interesting ways.

When we start creating more advanced workflows through the Reactive Extensions, we also need ways of handling errors as well.  We have the ability do handle errors and compensate as they happen through a rich set of operators including Catch, Finally, Using, OnErrorResumeNext, Retry and more.

So with that, download it, and give the team feedback!

This entry was posted in Event-based Porgramming, JavaScript, JSConf, Reactive Framework. Bookmark the permalink. Follow any comments here with the RSS feed for this post.