I remember when I first learned JavaScript, I learned from the native DOM API, using the native DOM API to complete some additions, delets and changes, and then I learned JQuery.

When I first started working with JQuery, I thought this was pretty cool. Take this logic:

Create a P tag containing a text node and insert it into the Container.

Writing with the native DOM API looks like this:

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

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

containerDom.appendChild(paraDom);
Copy the code

With JQuery it looks like this:

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

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

Much simpler than writing using the native DOM API! That’s why JQuery was so popular back then.

Although Vue, React and other data-driven front-end frameworks are used to write pages, dom is not directly manipulated. However, it is still convenient to use JQuery for scenarios where you need to manipulate the DOM directly, such as active pages.

So what does JQuery do?

JQuery encapsulates the DOM, provides a number of APIS for manipulating the DOM, and supports chain calls, which can easily organize the DOM manipulation logic. It also supports plugins to customize methods to use in chain calls.

You may say, JQuery is basically useless, why mention it?

Because I think JQuery encapsulates this layer of DOM manipulation so well that it reduces the complexity of dom manipulation. In addition to dom manipulation, the front end often deals with asynchronism, such as XHR, Fetch, Event Listener, etc. Although Promise can be encapsulated, it can be further simplified into async/await writing. But Promise and async/await only change the writing form of asynchronous logic without reducing the complexity of writing asynchronous logic. Can we encapsulate asynchronous logic as JQuery encapsulates DOM operations to simplify the writing of asynchronous logic?

There is such a library, rx.js.

When we wrote JQuery, we wrapped the DOM in a layer such as const $container = $(dom), so that we could chain the logic together using JQuery’s built-in utility functions or functions extended by the plugin.

To distinguish them from DOM objects, we will name JQuery objects $xx, $YY, etc.

The first step in rx.js is to wrap the asynchronous logic in one layer:

That is, the Event Listener, Promise, callback functions such as asynchronous code package one layer:

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

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

// Wrap a layer of callback
const observeable3$ = Rx.Observable.bindCallback(fs.exists);
Copy the code

The wrapped object is not called an RxJS object, it’s called an Observable, and we’ll name it XXX $, yyy$for easy identification, just like the JQuery object is named $XXX, $yyy.

You can then use a set of built-in utility functions called operator:

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

For asynchronous logic, throttleTime is the built-in throttleTime operator, so you don’t need to write it yourself.

Common asynchronous logic such as ignoring the first three events take(3), mapping the data once map(() => XXX) and so on is easy to write with operators.

Organizing asynchronous logic into chains (or pipes), writing the processing logic at each step with operators and then concatenating them, turns the writing of asynchronous logic into the organization of pipes. And just as JQuery can be extended by writing plug-ins, Rxjs also supports custom operators.

After passing through the pipeline, the data goes through each step of the asynchronous logic, and we can listen through subcribe to get the final data.

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

Of course, there may be an error in the process of processing, that also need to pass the error, and finally after the processing will be notified, so we can write three cases of processing:

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

These processing logic are called observers.

That’s what RxJs does. Because asynchronous logic is a response to an event, this is also called reactive programming.

An Observable can be created by enclosing a layer of Event listeners, callbacks, and promises.

For example, we wrap a list of numbers as observables:

// Multiple data
const observable$ = Rx.Observable.of(1.2.3); 

// Multiple data in an array
const observable2$ = Rx.Observable.from([1.2.3]);
Copy the code

Or through some logic to produce a series of data:

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

Or this:

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

The difference here is that the Subject can call Next externally to generate data, whereas the New Observable calls Next inside the callback to generate data.

Let’s summarize:

Just as JQuery wraps a layer around the DOM and then provides apis to simplify DOM manipulation, RxJS wraps a layer around asynchronous logic and then provides a series of operators. We can wrap EventListenr, Promise, callback, etc. into Observables (or create observables with of, from, Subject, etc.), and then use the built-in or self-extended Oprator organization to process pipes. Use an Observer at the end of the pipe to accept data and handle errors. This transforms the writing of asynchronous logic into an organization of operator pipelines. Writing asynchronous logic becomes fast once you are comfortable with the built-in operators or have deposited some of your own.

RxJS encapsulates asynchronous logic and does not conflict with front-end frameworks such as Vue and React, so it works well together. (Angular even integrates RxJS by default.)

In Vue, for example, we can wrap the event as an Observable in A Subject, and then organize the asynchronous logic with RxJS operators:

<div @click="clickHandler"Let me < / div > >Copy the code
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)
      }
   }
}
Copy the code

In React, an Observale is created by the Subject to convert the writing of asynchronous logic to the assembly of operators:

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>}}Copy the code

We create an Observable with Subject that calls Next every time we click to generate a data feed into the processing pipeline.

The pipeline is organized by operator. We first cut off the flow for 500ms, then change the value to 1, and then count.

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

This completes the asynchronous logic of throttling + counting, which is essentially the assembly of the operator, which is what RxJS is all about.

conclusion

Dom manipulation with native DOM APIS is cumbersome, so we will use JQuery, which wraps the DOM in a layer, provides many convenient built-in apis, and supports extension through plug-ins, which greatly simplifies DOM manipulation.

In addition to manipulating the DOM, front-end development often writes asynchronous logic, which also requires a package-level library called Rx.js to simplify things.

Rx.js encapsulates events listeners, promises, and callbacks as Observables (you can create them yourself), provides operators (you can also customize them), and assembles them into pipes to handle asynchronous logic. Finally, the Observer is passed in to receive the data and handle errors. In this way, the writing of asynchronous logic is changed to the assembly of operator, and the filling in the blank is changed to the multiple-choice question. The writing speed and experience of asynchronous logic will naturally be improved a lot.

Moreover, RxJS is specifically designed to handle asynchronous logic and can be used well with front-end frameworks.

Just as manipulating the DOM in JQuery is great, writing (assembling) asynchronous logic in RxJS is great once you’re familiar with the RxJS operator.