RxJS is to asynchrony what JQuery is to dom

I remember when I first learned JavaScript, I learned from the native dom api. I used the native dom api to complete some functions of addition, deletion and modification, and then I will learn JQuery.

When I first came into contact with JQuery, I felt it was great. For example, this logic:

Create a p tag containing a text node and insert it into the container.

Using the native dom api to write is as follows:

const containerDom = document.getElementById('container');

const textDom = document.createTextNode("Hello World.");
const paraDom = document.createElement("p");
paraDom.appendChild(textDom);

containerDom.appendChild(paraDom);

It is written in JQuery as follows:

const $container = $('#container');

$container.append('<p>Hello World.</p>');

It's much simpler to write than using the native dom api! That's why JQuery was so popular in those days.

Although the data-driven front-end frameworks such as Vue and React are now used to write pages, they basically do not directly operate dom. However, it is still convenient to use JQuery when it involves some scenes that require direct operation of DOM, such as active pages.

What did JQuery do?

JQuery encapsulates dom in one layer, provides many APIs for operating dom, and supports chain call, which can easily organize dom operation logic, and also supports plug-ins to define some methods to be used in chain call.

You might say that JQuery is basically useless. Why do you mention it?

Because I think JQuery has a good encapsulation of dom operation, which reduces the complexity of dom operation a lot. In addition to frequently operating dom, the front end also often handles asynchrony, such as XHR, Fetch and Event Listener. Although Promise can be encapsulated and further simplified into async/await, Promise and async/await only change the writing form of asynchronous logic and do not reduce the complexity of asynchronous logic writing. Can we encapsulate asynchronous logic just as JQuery encapsulates dom operations and simplify the writing of asynchronous logic?

There is indeed such a library, Rx js.

When writing JQuery, we encapsulate a layer of dom, such as const container = (dom), so that we can use JQuery's built-in tool functions or some functions extended through plug-ins to string logic through chain calls.

In order to be easily distinguished from dom objects, we will name JQuery objects as, yy, etc.

So Rx The first step of JS is to layer the asynchronous logic package:

In other words, the asynchronous code package of Event Listener, Promise and callback function is one layer:

// Package layer 1 Event Listener
const observable$ = Rx.Observable.fromEvent(document.querySelector('button'), 'click'); 

// Pack a layer of Promise
const observable2$ = Rx.Observable.fromPromise(fetch('/users'));

// Packet layer 1 callback
const observeable3$ = Rx.Observable.bindCallback(fs.exists);

The wrapped object is not called RxJS object, but Observable object. In order to distinguish it, we will name it xxx and, just as we will name JQuery object yyy and yyy.

Then you can use a series of built-in tool functions, which are called operator:

observable$.pipe(
    throttleTime(300),
    take(3),
    map(event => event.target.value)
);

For example, we often do throttling for asynchronous logic, so we don't need to write it ourselves. Just use the built-in operator throttleTime.

In addition, ignoring the first three events take(3), mapping the data once, map (() = > XXX), and so on. These common asynchronous logics are easy to write with operators.

Organize the asynchronous logic into a chain (or pipe), write the processing logic of each step with operators, and then connect them in series, so as to turn the writing of asynchronous logic into the organization of pipe. And just as JQuery can be extended by writing plug-ins, Rxjs also supports custom operators.

After passing through this pipeline, the data is processed by asynchronous logic at each step. We can get the final data through subcribe monitoring.

observerable$.subscribe((value) => {
    // xxx
})

Of course, there may be an error in the process of processing, so the error should also be passed down, and there will be a notice after the final processing, so you can write the processing of three cases:

observerable$.subscribe({
    next: (v) => {},
    error: (err) =>{},
    complete: () => {}
});

These processing logic are called Observer.

This is what RxJs does. Because asynchronous logic is a response to an event, it is also called responsive programming.

The Observable we created just now includes a layer of Event Listener, callback and Promise. Of course, we can also directly create such an Observable object:

For example, we encapsulate a series of numbers into Observable:

// Multiple data
const observable$ = Rx.Observable.of(1, 2, 3); 

// Multiple data in array
const observable2$ = Rx.Observable.from([1,2,3]);

Or generate a series of data through some logic:

var observable$ = new Rx.Observable(function (observer) {
    observer.next(1);
    observer.next(2);
    observer.next(3);
    setTimeout(() => {
        observer.next(4);
        observer.complete();
    }, 1000);
});

Or so:

const observable$ = new Rx.Subject();
 
observable$.next(1);
observable$.next(2);

The difference here is that Subject can call next externally to generate data, while new Observable calls next within the callback function to generate data.

Let's summarize:

Just as JQuery wraps a layer of dom and then provides APIs to simplify dom operations, RxJS also wraps a layer of asynchronous logic and then provides a series of operators. We can package EventListener, Promise, callback, etc. into Observable (or create Observable by ourselves with of, from, Subject, etc.), then organize the processing pipeline with built-in or self extended oprator, and use Observer to accept data and handle errors at the end of the pipeline. In this way, the writing of asynchronous logic is transformed into the organization of operator pipeline. When you are proficient in the built-in operators or have precipitated some operators yourself, the speed of writing asynchronous logic will become very fast.

Because RxJS only encapsulates asynchronous logic and does not conflict with front-end frameworks such as Vue and React, it can be well combined. (Angular even integrates RxJS by default)

For example, in Vue, we can encapsulate events into an Observable with Subject, and then organize asynchronous logic with RxJS operators:

<div @click="clickHandler">Point me</div>
import { Subject } from 'rxjs'
import { debounceTime } from 'rxjs/operators'

export default {
   data() {
     return {
       observable$: new Subject()
     }
   },
   created() {
      this.observable$.pipe(debounceTime(500)).subscribe((event) => {
        // xxx
      })
   },
   methods: {
      clickHandler(event) {
         this.observable$.next(event)
      }
   }
}

The same is true in React. By creating an observer with Subject, the writing of asynchronous logic can be transformed into the assembly of operator:

class MyButton extends React.Component {
    constructor(props) {
       super(props);
       this.state = { count: 0 };

       this.observable$ = new Rx.Subject();
        
       this.observable$.pipe(
           debounceTime(500),
           map(() => 1),
           scan((total, num) => total + num, 0)
       );

       this.observable$.subscribe(x => {
          this.setState({ count: x })
       })
    }
    render() {
        return <button onClick={event => this.observable$.next(event)}>{
            this.state.count
        }</button>
    }
}

We use Subject to create an Observable object. Each click calls next to generate a data, which is passed into the processing pipeline.

We use the operator to organize the pipeline. We first make a 500ms closure, then change the value to 1, and then count.

After processing, the accumulated value is passed to the Observer, which can be set to state.

The asynchronous logic of throttling + counting is finished. In fact, the next operator is assembled, which is the meaning of RxJS.

summary

Using the native dom api for dom operation is cumbersome, so we will use JQuery, which packages the dom in one layer, provides many convenient built-in APIs, and also supports plug-in extension, which greatly simplifies the dom operation.

In addition to operating dom, front-end development often needs to write asynchronous logic. It also needs such a package level library to simplify, which is Rx js.

Rx.js encapsulates Event Listener, Promise, callback, etc. into Observable (you can also create them yourself), and provides many operators (you can also customize them). They are assembled into a processing pipe to process asynchronous logic, and finally passed into Observer to receive data and handle errors. In this way, the writing of asynchronous logic will be transformed into the assembly of operator, and the blank filling questions will be transformed into multiple-choice questions, which will naturally improve the writing speed and experience of asynchronous logic.

Moreover, RxJS is specialized in dealing with asynchronous logic and can be well combined with the front-end framework.

Just as it's great to operate dom with JQuery, it's also great to be familiar with the operator of RxJS and write (assemble) asynchronous logic with RxJS.

Added by stevietee on Thu, 03 Mar 2022 12:00:53 +0200