Chapter 13. Reactive Programming Using RxJS

In the previous chapter, we saw how .NET Reactive Extensions (Rx) aided natural programming in terms of composability, scalability, and responsiveness. We saw how streams enable natural state management with respect to time. Some of the constructs were dealt with in detail as well. More importantly, we saw how reactive constructs could be integrated seamlessly into the MVVM pattern, in terms of achieving data synchronization between View and Model via the View Model layer. Now, in this chapter, we will take a deep dive into the Reactive Extensions for JavaScript (RxJS) library, and look at how to write asynchronous and event-driven programs using observable collections. We will also take a detailed look at some interesting use cases, and their implementations with RxJS, to clearly understand how the RxJS library is leveraged to create concurrent and responsive applications. This will cover the reactive spectrum of web and Windows programming and, by the end of this chapter, you will be in a position to appreciate the possibilities of JavaScript and confidently leverage RxJS by having a jump start at it. This chapter includes the following:

  • A refresher on JavaScript execution context
  • An analogy of RxJS with YieldJS (a custom JS framework created by the authors)
  • A detailed outline on RxJS foundations and formalisms
  • Detailed code samples that demonstrate RxJS in action

The JS world

It is important to recap one's understanding of some of the JavaScript world's state of affairs. The most important aspect is that the language is single-threaded and, given this, the only option left for developers to write asynchronous code is by using callbacks, promises, and events. As a developer, you should be comfortable with the functional programming aspects of JS (in addition to its innate dynamic nature), including closures, higher-order functions, anonymous functions, and extension methods (augmenting types). We also cannot discount the advancements that have been made in the language core itself, with Node.js now becoming a preferred backend for serious scalability involving async I/O. Let's have a quick peek into these core concepts before we dive into RxJS.

As we all know (we presume you do), functions are objects in JavaScript, and they are first-class citizens, which makes JS a functional programming language as well (although it is formally classified as a dynamic language). We already discussed and saw in Chapter 10, Pattern Implementation Using Object/Functional Programming (that too, in depth), how higher-order functions, along with closures, enable you to compose programs naturally, now that we have been thinning the OOP wall for some time (at least from Chapter 10 onwards). Well, we don't intend to imply that JS is an exception.

Note

JS supports OOP through prototypal inheritance compared to classic inheritance in C#. This is important to understand how types can be augmented (analogous to our extension methods in C#).

Let's look at a higher-order function, which leverages callback (yet another function, which typically gets called for notification and continuation purposes):

    function onComplete(message) { 
      console.log(message); 
    } 
 
    function doSomething(onComplete) { 
      //Do what is needed here and then trigger onComplete 
      onComplete("Complete!"); 
    } 
 
    doSomething(onComplete); 

Note

In the preceding code snippet, the callback (onComplete) is passed as an attribute to a function (doSomething), thus making it (doSomething) a higher-order function.

Now let's look at a Closure in action:

    function add(x) { 
      return function (y) { 
        return x + y; 
      }; 
    }; 
 
    var addTen = add(10); 
    console.log(addTen(5));     //Returns 15 
    console.log(add(2)(3));     //Returns 5 

In the preceding code, add is a higher-order function (as it can be assigned to a variable), and x becomes a closure (lexical closure) within this function (irrespective of any nested/sub functions within). The results presented as comments against the respective statements speak for themselves. Also, see how anonymous functions (in this case, the inner function within the add function) are so natural in the JS realm. If you remember (from Chapter 9, Functional Programming Techniques for Better State Management), this also touches upon currying (an important concept in the functional programming world, which we've already dealt with in detail), a formalism that breaks any function with arity greater than one into a series of function evaluations (each accepting one argument at a time).

Another important concept to understand is augmenting types in JS. This provides an amazing way to impart capabilities to a type (just like the way we use extension methods in C#). Let's quickly look at the power of this, using the following example in terms of dynamically adding error-handling capability to functions (ones that don't have it) at runtime. Ever thought of that?

    Function.prototype.throws = function () { 
      var slice = Array.prototype.slice, 
        args = slice.apply(arguments), 
          errorMessage = args[0], 
            fn = this; 
      return function () { 
        try { 
          return fn.apply(null, args.slice(1)); 
        } 
        catch (e) { 
          console.log(errorMessage); 
          //Do what is needed apart from logging 
        } 
      } (); 
    }; 

This preceding code essentially augments a function object by adding the throws method to its prototype object (the base object). It deftly leverages some nifty features in the JS language (including array slicing, arguments object, function context capture using closure, and the apply method used for function evaluation). We suggest you look it up in case you are unable to follow the code. The candidate function, which deliberately throws an exception and one that should gracefully exit function evaluation is as follows:

    function errorSimulator(a, b) { 
      console.log(a, b); 
      return (parseInt(RxJS,10));//This statement forces an error! 
    } 

The following code (line 1) can be used in place of direct function evaluation (line 2), where the program throws a runtime error (ReferenceError: RxJS is not defined) and breaks execution. What is important to note is the preservation of arity for these functions despite using the decorator. This is where you will understand the relevance of the arguments object and the apply method. Here, our decorator assumes the semantics - that of issuing the first argument with the error message followed by the function arguments in the exact same order! You can easily achieve this with a find and replace in your code file, can't you?

    errorSimulator.throws("I have you!", 2, 0);  // line 1
    errorSimulator(2, 0);                        // line 2

We guess that now you've got a handle on how things work in the JS world. It's important to know these as we move forward to explore reactive programming using RxJS.

To try out the code samples in this chapter, please ensure you have Node.js set up and RxJS dependencies installed through npm, as follows:

    npm install rx 
    [email protected] node_modules
x 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.33.87