I’m sure you’ll be curious as you learn about the new technique of responsive programming, especially its variants, such as Rx, Bacon.js, RAC, and so on.

In the absence of good materials, the learning process of reactive programming is fraught with challenges. At first, I tried to find a few tutorials, but found only a handful of practical guides, and they were so shallow that no one ever took up the challenge of building a complete body of knowledge around responsive programming. In addition, official documentation often doesn’t do a very good job of helping you understand certain functions, as they often seem convoluted. See here:

Rx.Observable.prototype.flatMapLatest(selector, [thisArg])

Map each element in the observable sequence to a new observable sequence according to the element subscript, and then… %………………… & % $# @ @… & * * (dizzy)

Oh, my God, this is so convoluted!

I’ve read two books, one that just gives you a great picture of reactive programming, and one that just goes into how to use reactive libraries. I learned reactive programming the hard way, learning it a little better as I continued to build projects. I will use it in a real project at my company and get support from my colleagues when I have problems.

The hardest part of the learning process is how to think in a responsive way, but it means abandoning the old imperative and state typical programming habits and forcing your brain to work in a different paradigm. I haven’t found a single tutorial on the web that looks at this level, and I think the world deserves a good hands-on tutorial that teaches you how to think responsive programming, so that you can start learning responsive programming. Then look at the various library documentation to give you more guidance. Hopefully this article will help you get a quick start in the world of responsive programming.

“What is reactive programming?”

There are a lot of bad explanations and definitions on the web. Wikipedia is often very general and theoretical, Stackoverflow is often very prescriptively written, and Reactive Manifesto looks like something for your PM or boss. Microsoft’s Rx terminology, “Rx = Observables + LINQ + Schedulers”, is also too heavy and full of Microsoft-like things to confuse us even more. Terms like “Reactive” and “Propagation of change” do not convey any meaningful concepts relative to the MV* framework you use and your preferred programming language. Of course, my View framework reacts from the Model, and my changes propagate of course, without which my interface would have nothing to render at all.

So, no more of this crap.

Reactive programming is the programming paradigm for interacting with asynchronous data flows

On the one hand, this is nothing new. Event Buses or some typical click Event is essentially an asynchronous Event stream so you can watch it change and do some side effects. Reactive is the idea that you can create data streams for anything other than clicking and hovering events. Data flow is everywhere, and anything can be a data flow, such as variables, user input, properties, caches, data structures, and so on. For example, you can think of your Twitter feed as a data stream like a click event, which you can listen to and react to.

Most importantly, you’ll have some amazing functions to combine, create, and filter any set of data streams. This is the magic of functional programming. One data stream can serve as input to another, or even multiple data streams can serve as input to another data stream. You can merge two streams, filter one stream to produce another that contains only the events you are interested in, and map the values of one stream to a new stream.

Data flow is the core of the whole responsive programming system. If you want to learn responsive programming, of course, you have to go into data flow first. So let’s start with the familiar “click a button” stream of events

A data flow is a chronological sequence of Ongoing events ordered in time. As shown above, it can emit three different events (already called events in the previous sentence) : a value event of some type, an error event, and a completion event. When a completion event occurs, in some cases we might do something like close the window or view component that contains the button.

We can only capture emitted events asynchronously, allowing us to execute one function on a value event, one function on an error event, and another function on a completion event. Sometimes you can ignore the latter two events and just focus on how to define and design the function to execute when the value event is emitted. The process of listening to this stream of events is called a subscription, the function we define is called an observer, and the stream of events can be called the observed topic (or observed). You should be aware of that, right, it’s the observer model.

The diagram above can also be redrawn in ASCII format, please note that we will continue to use it for the rest of the tutorial:

- a - b - c - d - X | - > a, b, c, d is the value of X is the error events | is complete event - > time line (shaft)Copy the code

Now that you’re familiar with reactive programming event streams, let’s not bore you with something new: We’ll create a new click event stream that evolved from the original click event stream.

First, let’s create an event flow that records the number of button clicks. In common reactive libraries, each event stream is accompanied by functions such as map,filter, scan, etc. When you call one of these methods, such as clickstream.map (f), it returns a new event stream based on the click event stream. It does not make any changes to the original click event stream. This property is called immutability, and it can be used with a responsive stream of events, like soymilk and churro. This can be done as a chained function, such as clickstream.map (f).scan(g):

  clickStream: ---c----c--c----c------c-->
               vvvvv map(c becomes 1) vvvv
               ---1----1--1----1------1-->
               vvvvvvvvv scan(+) vvvvvvvvv
counterStream: ---1----2--3----4------5-->
Copy the code

The map(f) function maps each return value from the original event stream to the new event stream based on the f function you provided. In the case above, we map each click event to the number 1, and scan(g) aggregates the previously mapped values and processes them according to the X = g(Current) algorithm, which in this case is a simple addition function. Then, when a click event occurs, the counterStream function reports the current total number of click events.

In order to show the true beauty of reactive programming, we assume that you have a “double click” event flow, in order to make it more interesting, we assume that the flow of events and deal with “three clicks” or “click the” event, and then take a deep breath to think about how to use the traditional status and imperative to handle way, and I’m willing to bet, doing so would rather nasty, There are variables involved to hold the state, and there are time interval adjustments.

Reactive programming is very succinct; in fact, the logical part only needs four lines of code. However, let’s ignore the code for now. Whether you’re a novice or an expert, looking at diagrams and thinking about them is a great way to understand and establish the flow of events.

In the figure, the gray box represents the functional process of replacing the above event stream with the following event stream. First, according to the event silence interval of 250 ms. The event-free period, the interval between the occurrence of the previous event and the occurrence of the next event) separates the click event stream segment by segment, and adds one or more click events from each segment to the list (this is the function: Buffer (stream.throttle(250ms)) does something, so let’s not get into the details right now, let’s just focus on the responsive part). Now we have multiple lists containing event streams, and we use the function in map() to calculate the integer value of each list length to map to the next event stream. Finally, we use the filter(x >= 2) function to ignore integers less than 1. So, in three steps we generate the desired event stream, and then we can subscribe (” listen “) to the event and do what we want.

I hope you get a sense of the elegance of this example. Of course, this example is just the tip of the iceberg of the magic of reactive programming, and you can also apply these three steps to different kinds of event flows, such as a stream of events in response to an API. On the other hand, there are so many functions you can use.

“Why should I do reactive programming?”

Reactive programming can deepen the abstraction of your code, allow you to focus on defining the business logic that depends on events rather than on implementation details, and make your code simpler.

Especially for popular Webapps and mobile apps, where UI events and data interact frequently, the benefits of reactive programming will become more obvious when developing these applications. Ten years ago, Web pages interacted by submitting a long form data to the back end and then doing some simple front-end rendering. However, today’s Apps have evolved to be more real-time: just modifying a single form field can automatically trigger the code saved to the back end, just as a user likes some content, it can be reflected to other connected users in real time, etc.

Today’s Apps are rich in real-time events to ensure an efficient user experience, so we need to adopt an appropriate tool to deal with it, and responsive programming is exactly the answer we want.

Examples of thinking in responsive programming

Let’s dive into some real examples, one that teaches you step by step how to think in a responsive programming way, no fictional examples, no half-baked concepts. By the end of this tutorial we will have generated some actual function code and be able to see why each step did what it did.

I have chosen JavaScript and RxJS as the programming languages for this tutorial for the following reasons: JavaScript is the language most people are familiar with, and Rx series libraries are widely used in many languages and platforms. Examples are.net, Java, Scala, Clojure, JavaScript, Ruby, Python, C++, Objective-C/Cocoa,Groovy, etc. So, no matter what language, library, or tool you’re using, you can learn (and benefit from) from this tutorial.

Implement a Who to follow feature

There is a UI element in Twitter that suggests users you can follow, as shown below:

We will focus on imitating its main functions, which are:

  • To start, load the recommended user account data from the API, and then display the three recommended users
  • Click Refresh to load the other three recommended users to display in the current three lines
  • Click the ‘X’ button on each row of recommended users to clear the current clicked user and display a new user to the current row
  • Each line shows a user’s picture and, when clicked, links to their home page.

We can ignore the other functions and buttons because they are secondary. Since Twitter recently shut down unauthorized public API calls, we’ll use Github’s API to get users instead and build our UI from there.

If you want to see the final result first, here’s the finished code.

The Request and Response

How do you deal with this in Rx? Before we begin, understand that (almost) everything can be a stream of events. This is a MANTRA of Rx. Let’s start with the simplest functionality: “To start, load the recommended user account data from the API, and then display three recommended users”. There is nothing special about this functionality. The simple steps are: (1) make a request, (2) get the response data, and (3) render the response data. Ok, let’s think of the request as a flow of events. You might think this is a bit of an exaggeration at first, but don’t worry, we have to start with the basics, don’t we?

To begin with we only need to make a request, and if we treat it as a data stream, it can only be a stream of events that returns a single value. We’ll have more requests to make later, but for now, there’s only one.

-- -- -- -- -- -- - | a - > is a string: 'https://api.github.com/users'Copy the code

This is a STREAM of URL events that we are going to request. Each time a request occurs, it tells us two things: when and what was done. Events are issued whenever a request is executed. And what is done is what is requested, which is the REQUESTED URL string.

In Rx, it is very simple to create an event stream that returns a value. Actually, the term for an event stream in Rx is “observed”, which means it can be observed, but I find that a silly name, so I prefer to call it an event stream.

var requestStream = Rx.Observable.just('https://api.github.com/users');Copy the code

But right now, this is just a string stream of events and nothing else, so we need to do something that we want to do when we emit this value, which we do by subscribing to the event.

requestStream.subscribe(function(requestUrl) {
  // execute the request
  jQuery.getJSON(requestUrl, function(responseData) {
    // ...
  });
}Copy the code

Note that we are using JQuery’s AJAX callback method (we assume you already know JQuery and AJAX) to handle this asynchronous request operation. But wait a minute, Rx is designed to handle asynchronous data streams. Can’t it handle data streams from a request in response at some point in the future? Well, it could in theory. Let’s give it a try.

requestStream.subscribe(function(requestUrl) { // execute the request var responseStream = Rx.Observable.create(function  (observer) { jQuery.getJSON(requestUrl) .done(function(response) { observer.onNext(response); }) .fail(function(jqXHR, status, error) { observer.onError(error); }) .always(function() { observer.onCompleted(); }); }); responseStream.subscribe(function(response) { // do something with the response }); }Copy the code

The rx.Observable.create () operation creates its own custom event stream and notifies each observer (or subscriber) of the event by displaying both data events (onNext()) and error events (onError()). What we did was just a little wrapper around the jQuery Ajax Promise. Wait, does this mean that the jQuery Ajax Promise is essentially an Observable?

Yes.

Promise++ is an Observable, and in Rx you can do something like this: Var stream = Rx. Observables. FromPromise (promise), promise can easily be converted to an observer (observables), was a very simple operation can let’s start using it now. The difference is that none of these being watched are compatible with Promises/A+, but in theory they are not in conflict. A Promise is a simple observed object with a single return value, and Rx goes a step further by allowing multiple returns.

It’s better that way, it’s more prominent that the observed is at least more powerful than Promise, so if you believe what Promise promises, keep an eye out for what responsive programming can do.

Now going back to the example, you should quickly notice that we are calling the subscribe() method again inside the subscribe() method, which is sort of like callback hell, The responseStream creation also depends on the requestStream. As mentioned earlier, there are many simple mechanisms in Rx to transform other event streams and create new ones, so we should try this too.

One of the most basic functions you need to know now is map(f), which takes each value from event stream A, performs the f() function on each value, and then populates the resulting new value into event stream B. If we apply this to our request and response event flow, we can map the request URL to a response Promises (disguised as a data flow).

var responseMetastream = requestStream
  .map(function(requestUrl) {
    return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
  });Copy the code

Then we created a monster called “Metastream” : an event stream loaded with an event stream. Don’t panic, metastream is the value of each issue is another event stream flow of events, and think you see it as a pointer (Pointers)] ((en.wikipedia.org/wiki/Pointe…

A responsive Metastream does look confusing and doesn’t seem to be helping us at all. We just want a simple response data stream, where each emitted value is a simple JSON object, not a ‘Promise’ JSON object. Ok, let’s take a look at another function: Flatmap, which is another version of the map() function and is flatter than Metastream. All events emitted in the “main trunk” event stream will be emitted in the “branch” event stream. Flatmap is not a fix for Metastreams, and MetaStreams is not a bug. Both are great tools for handling asynchronous response events in Rx.

var responseStream = requestStream
  .flatMap(function(requestUrl) {
    return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
  });Copy the code

It’s nice because our response event flow is defined according to the request event flow, and if we have more events in the request event flow in the future, we will also receive response events in the corresponding response event flow, as expected:

RequestStream: - a - b - c -- -- -- -- -- - -- -- -- -- -- - | - > responseStream: -- -- -- -- -- -- -- -- -- -- -- -- -- b -- -- -- -- -- -- - | c - > (lowercase is request event flow, capital flow) is a response to an eventCopy the code

Now we finally have a stream of events that respond and can render with the data we receive:

responseStream.subscribe(function(response) {
  // render `response` to the DOM however you wish
});Copy the code

Let’s put all the code together and see:

var requestStream = Rx.Observable.just('https://api.github.com/users');

var responseStream = requestStream
  .flatMap(function(requestUrl) {
    return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
  });

responseStream.subscribe(function(response) {
  // render `response` to the DOM however you wish
});Copy the code

The refresh button

I didn’t mention that the JSON data for this response is a list of 100 user data. This API only allows you to specify page offset, not page size. We only used 3 user data and wasted 97 others. Later we’ll learn how to cache the data for the response.

Each time the refresh button is clicked, the request event stream emits a new URL value so that we can retrieve the new response data. Here we need two things: the flow of events that hit the refresh button (rule: Everything works as a flow of events), and we need the flow of events that hit the refresh button as a dependency on the flow of requests (that is, the flow of events that hit the refresh button causes the flow of requests). Fortunately, RxJS already has methods that can be converted from event listener to observed.

var refreshButton = document.querySelector('.refresh');
var refreshClickStream = Rx.Observable.fromEvent(refreshButton, 'click');Copy the code

Since the refresh button click event does not carry the URL of the API to be requested, we need to map each click to an actual URL. Now we replace the request event stream with a click event stream, and map each click to a random page offset parameter to make up the API URL.

var requestStream = refreshClickStream
  .map(function() {
    var randomOffset = Math.floor(Math.random()*500);
    return 'https://api.github.com/users?since=' + randomOffset;
  });Copy the code

Because I was stupid and didn’t use automated tests, I just screwed up a feature I had done earlier. This way, the request is not executed in the first place, but only when the click event occurs. What we need is a request that executes in both cases: a request that executes both when you first open the web page and when you hit the refresh button.

We know how to do a separate stream of events for each case:

var requestOnRefreshStream = refreshClickStream
  .map(function() {
    var randomOffset = Math.floor(Math.random()*500);
    return 'https://api.github.com/users?since=' + randomOffset;
  });

var startupRequestStream = Rx.Observable.just('https://api.github.com/users');Copy the code

But can we combine these two into one? Yes, this can be done by using the merge() method. The following illustration illustrates the use of the merge() function:

stream A: ---a--------e-----o----->
stream B: -----B---C-----D-------->
          vvvvvvvvv merge vvvvvvvvv
          ---a-B---C--e--D--o----->
Copy the code

It should be easy to do now:

var requestOnRefreshStream = refreshClickStream
  .map(function() {
    var randomOffset = Math.floor(Math.random()*500);
    return 'https://api.github.com/users?since=' + randomOffset;
  });

var startupRequestStream = Rx.Observable.just('https://api.github.com/users');

var requestStream = Rx.Observable.merge(
  requestOnRefreshStream, startupRequestStream
);Copy the code

There is also a cleaner way to write this without the intermediate event flow variable:

var requestStream = refreshClickStream
  .map(function() {
    var randomOffset = Math.floor(Math.random()*500);
    return 'https://api.github.com/users?since=' + randomOffset;
  })
  .merge(Rx.Observable.just('https://api.github.com/users'));Copy the code

It can be even shorter and more readable:

var requestStream = refreshClickStream
  .map(function() {
    var randomOffset = Math.floor(Math.random()*500);
    return 'https://api.github.com/users?since=' + randomOffset;
  })
  .startWith('https://api.github.com/users');Copy the code

The startWith() function does exactly what you would expect. No matter what your input event stream is, the output of the startWith(x) function will be an x. But I’m not repeating the code (DRY) all the time, I’m just repeating the API URL string, and the improvement is to move the startWith() function to refreshClickStream so that it simulates a refresh button click event at startup.

var requestStream = refreshClickStream.startWith('startup click')
  .map(function() {
    var randomOffset = Math.floor(Math.random()*500);
    return 'https://api.github.com/users?since=' + randomOffset;
  });Copy the code

Yes, if you go back to the “botched automated tests” area and compare the two, you’ll see that I just added the startWith() function.

Model the three recommended user data with event streams

Until now, we have only touched on the UI recommended for attention in the rendering steps that occur in response to the subscribe() function of the event stream. Now that we have the refresh button, we have a problem: When you click the refresh button, the current three recommendations focus on the user is not clear, and as long as the response data reach after we got the new recommendations focus on user data, in order to make the UI look more beautiful, we need to click the refresh button when the incident clearly the current three recommendations focus on the user.

refreshClickStream.subscribe(function() {
  // clear the 3 suggestion DOM elements 
});Copy the code

No, man, not so fast. We have a new problem because we now have two subscribers influencing the UI DOM element that we recommend attention to (the other is responsestream.subscribe ()), which does not seem to comply with the Separation of concerns, Remember the principle of reactive programming?

Now, let’s model the user data for recommendation concerns into a stream of events, where each emitted value is a JSON object containing the user data for recommendation concerns. We will treat these three user data separately. The following is the event flow of user no. 1 data that we recommend to pay attention to:

var suggestion1Stream = responseStream
  .map(function(listUsers) {
    // get one random user from the list
    return listUsers[Math.floor(Math.random()*listUsers.length)];
  });Copy the code

Others, like suggestion2Stream, which are recommended, and suggestion3Stream, which are recommended, can be easily copied and pasted from suggestion1Stream. This isn’t duplicate code, just to keep our example simple, and I think it’s a good example to think about how to avoid duplicate code.

Instead of having the rendering happen in responseStream’s subscribe(), we do that here:

suggestion1Stream.subscribe(function(suggestion) {
  // render the 1st suggestion to the DOM
});Copy the code

We’re not handling the render in the subscribe() responseStream, we’re handling it like this:

suggestion1Stream.subscribe(function(suggestion) {
  // render the 1st suggestion to the DOM
});Copy the code

Going back to “clear out current recommendation followers when refreshing,” we could easily map the refresh hits to no NULL suggestion data, and including that in suggestion1Stream, as follows:

var suggestion1Stream = responseStream .map(function(listUsers) { // get one random user from the list return listUsers[Math.floor(Math.random()*listUsers.length)]; }) .merge( refreshClickStream.map(function(){ return null; }));Copy the code

When rendering, we interpret null as “no data” and hide the UI elements.

suggestion1Stream.subscribe(function(suggestion) { if (suggestion === null) { // hide the first suggestion DOM element }  else { // show the first suggestion DOM element // and render the data } });Copy the code

Now we have the following schematic diagram:

refreshClickStream: ----------o--------o---->
     requestStream: -r--------r--------r---->
    responseStream: ----R---------R------R-->   
 suggestion1Stream: ----s-----N---s----N-s-->
 suggestion2Stream: ----q-----N---q----N-q-->
 suggestion3Stream: ----t-----N---t----N-t-->
Copy the code

N is null

As a supplement, we can render the empty recommendation at the beginning. This is done by adding startWith(null) to the recommended event stream:

var suggestion1Stream = responseStream
  .map(function(listUsers) {
    // get one random user from the list
    return listUsers[Math.floor(Math.random()*listUsers.length)];
  })
  .merge(
    refreshClickStream.map(function(){ return null; })
  )
  .startWith(null);Copy the code

The result is this:

refreshClickStream: ----------o---------o---->
     requestStream: -r--------r---------r---->
    responseStream: ----R----------R------R-->   
 suggestion1Stream: -N--s-----N----s----N-s-->
 suggestion2Stream: -N--q-----N----q----N-q-->
 suggestion3Stream: -N--t-----N----t----N-t-->
Copy the code

Closing and using cached response data of recommended concern

The only feature left unimplemented is that each recommended user UI will have an ‘X’ button to close itself and then load another recommended user in the current user data UI. The original idea was that clicking any close button would require making a new request:

var close1Button = document.querySelector('.close1');
var close1ClickStream = Rx.Observable.fromEvent(close1Button, 'click');
// and the same for close2Button and close3Button

var requestStream = refreshClickStream.startWith('startup click')
  .merge(close1ClickStream) // we added this
  .map(function() {
    var randomOffset = Math.floor(Math.random()*500);
    return 'https://api.github.com/users?since=' + randomOffset;
  });Copy the code

This doesn’t work, it closes and reloads all recommendation followers, not just the one we clicked on. There are several ways to solve this problem and make it interesting, and we will reuse the previous request data to solve this problem. The per-page size of the API response is 100 user data, and we only used three of those, so there’s a lot of unused data to work with without having to ask for more.

Ok, again, let’s think of it in terms of event flow. When the ‘close1’ click occurs, we want to use the recently issued response data and call responseStream to pull a random user data from the response list, as follows:

    requestStream: --r--------------->
   responseStream: ------R----------->
close1ClickStream: ------------c----->
suggestion1Stream: ------s-----s----->
Copy the code

In Rx a combinatorial function called combineLatest should be what we need. The function takes data streams A and B as input, and whichever data stream emits A value, the combineLatest function takes the most recent values A and B emitted from the two data streams as input to f and returns an output value (c = f(x,y)). The following diagram makes the process of this function clearer:

stream A: --a-----------e--------i--------> stream B: -- -- -- -- -- -- b c d -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- q > VVVVVVVV combineLatest (f) VVVVVVV - AB - AC - EC - ED - ID - IQ -- -- -- -- > f is a function of converted into capitalCopy the code

With that, we can use the combineLatest() function on close1ClickStream and responseStream, and as soon as the close button is clicked, we can get recent response data and generate a new value on suggestion1Stream. On the other hand, the combineLatest() function is also relative: Every time a new response is issued on responseStream, it combines a new click-close button event to generate a new recommended user data, which is interesting, because it can simplify the code for our suggestion1Stream:

var suggestion1Stream = close1ClickStream
  .combineLatest(responseStream,             
    function(click, listUsers) {
      return listUsers[Math.floor(Math.random()*listUsers.length)];
    }
  )
  .merge(
    refreshClickStream.map(function(){ return null; })
  )
  .startWith(null);Copy the code

Now, we’re missing a piece of the puzzle. The combineLatest() function uses the two nearest data sources, but cannot generate a data event on the output stream if either data source hasn’t emitted anything yet. If you look at the ASCII chart above (the first chart in this article), you can see that there is no output when the first stream emits a value of A, and only an output when the second stream emits a value of B.

There are several ways to solve this problem, but we’ll use the simplest one, which simulates the ‘close 1’ click event at startup:

var suggestion1Stream = close1ClickStream.startWith('startup click') // we added this
  .combineLatest(responseStream,             
    function(click, listUsers) {l
      return listUsers[Math.floor(Math.random()*listUsers.length)];
    }
  )
  .merge(
    refreshClickStream.map(function(){ return null; })
  )
  .startWith(null);Copy the code

encapsulated

We are done, and here is the complete sample code wrapped:

var refreshButton = document.querySelector('.refresh');
var refreshClickStream = Rx.Observable.fromEvent(refreshButton, 'click');

var closeButton1 = document.querySelector('.close1');
var close1ClickStream = Rx.Observable.fromEvent(closeButton1, 'click');
// and the same logic for close2 and close3

var requestStream = refreshClickStream.startWith('startup click')
  .map(function() {
    var randomOffset = Math.floor(Math.random()*500);
    return 'https://api.github.com/users?since=' + randomOffset;
  });

var responseStream = requestStream
  .flatMap(function (requestUrl) {
    return Rx.Observable.fromPromise($.ajax({url: requestUrl}));
  });

var suggestion1Stream = close1ClickStream.startWith('startup click')
  .combineLatest(responseStream,             
    function(click, listUsers) {
      return listUsers[Math.floor(Math.random()*listUsers.length)];
    }
  )
  .merge(
    refreshClickStream.map(function(){ return null; })
  )
  .startWith(null);
// and the same logic for suggestion2Stream and suggestion3Stream

suggestion1Stream.subscribe(function(suggestion) {
  if (suggestion === null) {
    // hide the first suggestion DOM element
  }
  else {
    // show the first suggestion DOM element
    // and render the data
  }
});Copy the code

You can see a demo project here

The above snippet is small but does a lot: it uses the separation of concerns principle in place to manage multiple streams of events and even cache response data. This functional style makes the code look more like declarative programming than imperative: we’re not giving a set of instructions to execute, we’re just defining the flow of events to tell it what it is. For example, we used Rx to tell the computer that suggestion1Stream is a data stream combining the ‘close 1’ event with a user’s data from the latest response data, and null when the refresh event occurs and the program starts.

Notice the absence of flow-control statements such as if, for, while, or callback-based flow-control as is typical in JavaScript. If you can (I’ll leave you some implementation details as an exercise later), you can even use the filter() function on subscribe() to get rid of if and else. In Rx, we have data flow functions such as MAP, Filter, Scan, Merge, combineLatest, startWith, and many other functions that can be used to control the flow of event-driven programs. The collection of these functions allows you to achieve more power with less code.

The following

If you think Rx is going to be your preferred reactive programming library, then you need to spend some time getting familiar with the vast array of functions used to morph, combine, and create the observed. If you want to get familiar with these functions in event flow diagrams, take a look at this one: RxJava’s Very Useful Documentation with Marble Diagrams. And remember, whenever you have a problem, you can draw these pictures, think about it, look at this whole bunch of functions, and think about it. In my personal experience, this works very well.

Once you get started with Rx programming, remember that it is essential to understand the concept of Cold vs Hot Observables, and if you neglect it, it can come back and bite you in the face. I’ve warned you here that learning functional programming can improve your skills and familiarize yourself with common problems, such as the side effects of Rx

But the reactive programming library is not just Rx; there are also relatively easy to understand bacon.js without the quirks of Rx. Elm Language supports reactive programming in its own way: it is a reactive programming Language that compiles to Javascript + HTML + CSS and has a Time Travelling Debugger function, which is nice.

Rx is great for things like front-end and apps that need to handle a lot of programming. But it can be used not only on the client side, but also on the back end or near the database. In fact, RxJava is the component of the Netflix server API that handles parallelism. Rx is not limited to an application or programming language framework, it’s really a great programming paradigm to follow for any event driver you write.