Reactive Terminology & RxJS
Reactive Programming is a Paradigm that offers a unique approach to solving software problems. Data is described as a stream of events, called an Observable. These streams can be manipulated by chaining Operators to produce new Observables. Finally a Subscriber can be attached to an Observable to produce an end result or side effect. Applying this paradigm leads to more expressive code that tends to describe what is being done, instead of how it is being done, which is so prevalent in Imperative Programming.
Can be thought of as the primitive type of Reactive programming. An Observable is an object that produces values. There are two types of Obervables:
Hot Observables will produce values regardless of whether there are any subscribers to consume these values. Late subscriptions will not receive any historical events.
Cold Observables are lazy and will only start producing values once a subscriber is attached. All the subscribers will receive all the events produced by the Cold Observable. Another way to look at it, is that the generator function of the Observable will run for every new subscriber.
Apart from producing values, Observables can produce two more signals: error to indicate that the Observable failed to produce values and complete to signal that no more values will be produced.
RxJS includes many factory methods to create Observables from common sources.
An Operator is a special kind of Observable, it does not produce values in itself, instead it operates on the values produced by another Observable. Put differently, an Operator is an Observable that wraps another Observable.
RxJS includes plenty of built in operators.
Subscribers sit at the end of the pipeline, after all the configured Operators have mutated the values produced by the Observable. As mentioned earlier, when a new Subscriber is attached to a Cold Observable, the generator function of that Observable will be re-run.
Example: Reverse Indent Files
A fun example of applying RxJS is a node script that reverse indents files. How is this useful you ask? Think of your first pull request on April Fools' Day with all the source files reverse indented! That will surely produce some interesting feedback.
The source for this example can be found on GitHub.
What do I mean by reverse indented files? Consider the following example,
given a file with the following content:
**** **** **** **** **** **** ****
The script will overwrite the file with:
**** **** **** **** **** **** ****
Step 1: Recursively Read Files In A Directory
Start by recursively reading all the files in a given directory. I made use of recursive-readdir to get the job done. The first step also makes use of the
Observable.bindNodeCallback factory method provided by RxJS to create the initial Observable.
Step 2: Map contents to an array of lines
The next step is to map the stream of files to a stream of objects with two properties, the file name and its contents as an array of lines. Later the file name will be used to write the result of the reverse indentation back to the file system.
Step 3: Find the longest line
The key step is to find the longest line in the file. This will be used to calculate how much whitespace to prefix every line with.
Step 4: Swap whitespace around
Once the longest line in the file is found, every line can be reverse indented by moving the whitespace at the beginning of the line to the end of the line and then padding the beginning of the line with whitespace until it is the same length as the longest line.
Step 5: Optionally add some logging to the pipeline
Peeking into the stream without causing any side effects can be achieved through the
do operator. In the gist below the original content of the file is logged in red, followed by the reverse indented lines logged in green. The output is only produced if the
verbose flag is passed in from the command line.
Step 6: Subscribe to the result and write the file.
Finally a subscriber is registered at the end of the stream to receive the results, one file at a time. Depending on whether the
dryRun flag was passed in from the CLI, the subscriber will either log the results or replace the contents of the original file with its reverse indented counterpart.