📜 ⬆️ ⬇️

Reactive Extensions for javascript. Complete guide

Want to use Observable Collections? Have you heard about Reactive Extensions? Like LINQ? Don't like writing spaghetti code? Need monads? And all this on javascript?

So what is Rx for JavaScript?
Rx stands for Reactive Extensions. As the definition on the project page says:
Rx is a library for creating asynchronous and event-oriented programs using collections, following the “Observer” pattern.

The main idea is to consider asynchronous computing as data sources. For example, a computer mouse is nothing but a source of clicks and cursor movements. To work with such a data set are Observable Collections . Since RxJS takes roots from the .NET world, therefore, some LINQ features (select, where, take, skip, etc.) become available to us. More on this later, but for now let's consider the structure of the library.

Integration with existing frameworks

First, download RxJS via the following link . Included are extensions for working with jQuery, MooTools, prototype, Dojo, ExtJS, as well as with popular services like Google Maps. The library itself consists of a single rx.js file of 7.4 KB in size (30 KB in uncompressed form).


')
In the examples, Visual Studio 2010 with the Web Developer package installed will be used as an editor. Client framework - jQuery.

So, create a simple HTML page and enable RxJS.

<head> <title>Samplestitle> <script type="text/javascript" src="Scripts\rx.js"></script> <head> 

After this, IntelliSense will start showing Rx methods.



A bit of theory

Before moving forward, I would like to clarify some of the terms and conventions adopted in Rx.

Observers

As mentioned above, the Subscribe method is used to subscribe to Observable events:

 var source = null; var subscription = source.Subscribe( function (next) { console.log("OnNext: " + next); }, function (exn) { console.log("OnError: " + exn); }, function () { console.log("OnCompleted"); }); 

and

 var source = Rx.Observable.Empty(); var observer = Rx.Observer.Create( function (data) { $(" ").text(data).appendTo("#content"); }); var handle = source.Subscribe(observer); 

Consider the Subscribe method in more detail:



To unsubscribe an observer there is no separate method. Instead, the Dispose pattern came to RxJS from the .NET environment.
So the link (handle) for Observer contains the Dispose method, by calling which you can destroy the object.



Observables

In most cases, you will always work with “hot” browsers: be it a web service call, or work with DOM elements. “Cold” browsers in real-world applications are not so common, but when unit testing, their purpose becomes clear - working with previously prepared data sets.
Below is the API table for creating Observable and their description.
Method nameDescription
Empty ()Returns an empty collection with OnCompleted call
Throw (error_msg)Returns an empty collection with a call to OnError
Return (value)Returns a collection from a single item.
Range (start, count)Returns an array of 32-bit Integer
Generate (initial_state, condition, selector, iterate)Returns a collection created by following parameters.
GenerateWithTime (time)Similar to the Generate method, but adds the time between the generation of elements
Never ()Returns an empty collection without triggering events.
toObservable (event_name)Extension to work with the jQuery object
FromArray ()Initializes a collection from a js array
Create (subscribe_detail)Creates a collection of observer subscription details.
FromDOMEvent (dom_element, event_name)Extension for working with DOM elements
FromIEEvent (dom_element, event_name)An extension for working with specific events of Internet Explorer's DOM elements
FromHtmlEvent (dom_element, event_name)Is a generic method for FromDOMEvent () and FromIEEvent ()

LINQ

So, we come to one of the most interesting features of RxJS - LINQ support. I think many have heard about this wonderful technology available on the .NET platform.
LINQ stands for Language Integrated Query, allowing you to query various collections. With the advent of RxJS, not all methods came to the world of JavaScript, but the most useful ones:

Added and new, present only here:

I will not give a complete list, because You can view the rest of the methods yourself.
For a clearer understanding of the picture below is a diagram of the event subscription:



Since jambda expressions do not exist in JavaScript, their role as parameters for LINQ methods performs normal functions:

 var items = [1, 2, 3, 4, 5]; var observable = Rx.Observable.FromArray(items).Select(function (item) { return { Value: item, Sqrt: Math.sqrt(item) }; }).Where(function (item) { return item.Value + 2 < 5; }); 

Returning to the new features (DistinctUntilChanged, Throttle, etc.) I would like to note their extraordinary usefulness. So Throttle will replace your use of setInterval and setTimeout in some situations, and using it with DistinctUntilChanged will reduce, for example, the number of ajax requests when entering text.
And visual code for example:

 var input = $("#textbox").toObservable("keyup") .Select(function (event) { return $(event.target).val(); }) .Timestamp() .Do(function (inp) { var text = "I: " + inp.Timestamp + "-" + inp.Value; $(" ").text(text).appendTo("#content"); }) .RemoveTimestamp() .Throttle(1000) .Timestamp() .Do(function (inp) { var text = "T: " + inp.Timestamp + "-" + inp.Value; $(" ").text(text).appendTo("#content"); }) .RemoveTimestamp() .DistinctUntilChanged(); var subscribtion = input.Subscribe(function (data) { $(" ").text("Your text: " + data).appendTo("#content"); }); 

Thus, before calling DistinctUntilChanged and transmitting an array of text field values, we perform some kind of logging.

Say no to spaghetti code!

In this last part of the article I would like to consider working with web services that have already been mentioned several times.
How many times have you written about this code:

 $.ajax({ url: "your_url", dataType: "json", success: function (data, textStatus, xhr) { $("#results").empty(); $.each(data[1], function (_, result) { $("#results").append(" " + result + " "); }); }, error: function (xhr, textStatus, errorThrown) { $("#error").text(errorThrown); } }); 

I think quite often. What is his problem? That's right, the data processing logic is encapsulated along with the definition of the data source, which turns into a “spaghetti code”.
RxJS comes to the rescue of this problem. First you need to connect the file rx.jQuery, which is an extension for integration with jQuery.
After that we will do a little refactoring of the above code:

 function serviceCall(text) { return $.ajaxAsObservable( { url: "your_url", dataType: "json", format: "json", data: text }); } var source = serviceCall("search me"); var handle = source.Subscribe(function (result) { $("#results").append(" " + result + " "); }, function (exn) { $("#error").text(exn); }); 

Thus, we have separated the data processing logic from the request itself, wrapping the request itself into an entity.

Afterword

This article wanted to show the power of the Reactive Extensions for JavaScript library. The scope of its application is quite extensive and is determined only by the needs of the programmer. From my own experience, I can say that the Observable Collections and the philosophy of RxJS itself require a revision of their already well-established code-writing practices, but it is worth it.
Unit testing, cancellation of operations, and the SelectMany operator (monads appear here) were not considered, but this will be discussed in the next article.

PS

A list of useful links that consider some points in more detail:

Source: https://habr.com/ru/post/132463/


All Articles