Today we will cover three very important operators, which can be seen in many examples of rxJs-related library usage. Many beginners will probably give up on seeing the three Operators when using these libraries, but if you read the series in its entirety, you should be able to accept and understand it by now.

Operators

concatMap

ConcatMap is simply a simplified form of map plus concatAll, so let’s go straight to an example

var source = Rx.Observable.fromEvent(document.body, 'click');

var example = source
                .map(e => Rx.Observable.interval(1000).take(3))
                .concatAll();

example.subscribe({
    next: (value) => { console.log(value); },
    error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }});Copy the code

The above example can be simplified to

var source = Rx.Observable.fromEvent(document.body, 'click');

var example = source
                .concatMap(
                    e => Rx.Observable.interval(100).take(3)
                );

example.subscribe({
    next: (value) => { console.log(value); },
    error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }});Copy the code

The two behaviors are consistent. Remember that concatMap will first process the previous observable and then the next Observable. The Marble Diagram is shown as follows

source : -----------c--c------------------...
        concatMap(c => Rx.Observable.interval(100).take(3))
example: -------------0-1-2-0-1-2---------...Copy the code

This behavior is also commonly used to send requests as follows

function getPostData() {
    return fetch('https://jsonplaceholder.typicode.com/posts/1')
    .then(res => res.json())
}
var source = Rx.Observable.fromEvent(document.body, 'click');

var example = source.concatMap(
                    e => Rx.Observable.from(getPostData()));

example.subscribe({
    next: (value) => { console.log(value); },
    error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }});Copy the code

JSBin | JSFiddle

Here, each time we click on the screen, an HTTP request will be sent. If we click in quick succession, you can see in the developer tools network that each request will not be sent until the previous request is completed, as shown below

It is recommended to set the simulation speed to the slowest

As can be seen from the graph of network, the sending time of the second request is after that of the first request. We can ensure that each request will wait for the completion of the previous request before processing it.

The second argument to concatMap is a selector callback, which takes four arguments

  1. An element sent by an external Observable
  2. An element sent by an internal Observable
  3. The external Observable sends the index of the element
  4. The internal Observable sends the index of the element

Return the value we want, as shown in the following example

function getPostData() {
    return fetch('https://jsonplaceholder.typicode.com/posts/1')
    .then(res => res.json())
}
var source = Rx.Observable.fromEvent(document.body, 'click');

var example = source.concatMap(
                e => Rx.Observable.from(getPostData()), 
                (e, res, eIndex, resIndex) => res.title);

example.subscribe({
    next: (value) => { console.log(value); },
    error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }});Copy the code

JSBin | JSFiddle

The external Observable sends the click Event instance, and the internal Observable sends the Response instance. Here we return the title property of the Response instance. This way we can receive the title directly, which is good for response when the value to be selected is related to the previous event or sequence index.

switchMap

SwitchMap is a simplified version of map and switch, as follows

var source = Rx.Observable.fromEvent(document.body, 'click');

var example = source
                .map(e => Rx.Observable.interval(1000).take(3))
                .switch();

example.subscribe({
    next: (value) => { console.log(value); },
    error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }});Copy the code

The above code can be simplified to

var source = Rx.Observable.fromEvent(document.body, 'click');

var example = source
                .switchMap(
                    e => Rx.Observable.interval(100).take(3)
                );

example.subscribe({
    next: (value) => { console.log(value); },
    error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }});Copy the code

The Marble Diagram is shown as follows

source : -----------c--c-----------------...
        concatMap(c => Rx.Observable.interval(100).take(3))
example: -------------0--0-1-2-----------...Copy the code

Just note that the switchMap unsubscribes the previous unprocessed Observable directly after the next Observable is sent. See the switch section in the previous article for details on this part.

Alternatively, we can use switchMap to send HTTP requests

function getPostData() {
    return fetch('https://jsonplaceholder.typicode.com/posts/1')
    .then(res => res.json())
}
var source = Rx.Observable.fromEvent(document.body, 'click');

var example = source.switchMap(
                    e => Rx.Observable.from(getPostData()));

example.subscribe({
    next: (value) => { console.log(value); },
    error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }});Copy the code

JSBin | JSFiddle

If we click five times in quick succession, we can see in the developer tools network that each request will be sent when clicked, as shown below

Gray is the browser’s native pause behavior. In fact, in gray, the fetch performs the request at the beginning, but it is stuck in the browser waiting to be sent.

As you can see from the figure above, even though we sent multiple requests, only one log will actually be printed, which means that the previous request doesn’t cause any side-effect. This is a good thing to use when you’re just looking at the last request, For example, auto Complete, we only need to display the last type of text the user typed on the screen to make suggestions instead of each time.

SwitchMap, like concatMap, has a second argument, selector callback, that can be used to pass back and forth the value we want. This part of switchMap behaves the same as concatMap, which we won’t go into here.

mergeMap

MergeMap is a simplified version of map plus mergeAll, as follows

var source = Rx.Observable.fromEvent(document.body, 'click');

var example = source
                .map(e => Rx.Observable.interval(1000).take(3))
                .mergeAll();

example.subscribe({
    next: (value) => { console.log(value); },
    error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }});Copy the code

The above code can be simplified to

var source = Rx.Observable.fromEvent(document.body, 'click');

var example = source
                .mergeMap(
                    e => Rx.Observable.interval(100).take(3)
                );

example.subscribe({
    next: (value) => { console.log(value); },
    error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }});Copy the code

Marble Diagram is shown

source : -----------c-c------------------...
        concatMap(c => Rx.Observable.interval(100).take(3))
example: -------------0-(10)-(21)-2----------...Copy the code

Remember that mergeMap can process multiple Observables in parallel. In this case, when we do two quick taps, the time points at which elements are sent overlap. For details on this, see the merge section.

Alternatively, we can use switchMap to send HTTP requests

function getPostData() {
    return fetch('https://jsonplaceholder.typicode.com/posts/1')
    .then(res => res.json())
}
var source = Rx.Observable.fromEvent(document.body, 'click');

var example = source.mergeMap(
                    e => Rx.Observable.from(getPostData()));

example.subscribe({
    next: (value) => { console.log(value); },
    error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }});Copy the code

JSBin | JSFiddle

If we click five times in quick succession, you can see in the developer tools network that each request will be sent when clicked and five instances will be logged, as shown below

MergeMap can also pass in a second selector callback, which is exactly the same as the second concatMap parameter, but the point of mergeMap is that we can pass in a third parameter to limit the amount of parallel processing

function getPostData() {
    return fetch('https://jsonplaceholder.typicode.com/posts/1')
    .then(res => res.json())
}
var source = Rx.Observable.fromEvent(document.body, 'click');

var example = source.mergeMap(
                e => Rx.Observable.from(getPostData()), 
                (e, res, eIndex, resIndex) => res.title, 3);

example.subscribe({
    next: (value) => { console.log(value); },
    error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }});Copy the code

JSBin | JSFiddle

Here we pass in 3 to limit HTTP requests to three at a time and wait for one to complete before processing the next, as shown below

Notice that I clicked five times in a row, but the fourth request was sent after the first request was completed, which is great for special requests to limit the number of requests that can be sent at the same time.

RxJS 5 also retains the mergeMap alias flatMap, although it is not officially documented, but the two methods are identical. Please refer here

switchMap, mergeMap, concatMap

The three Operators have another feature in common, that is, they can convert the promise instance returned by the first parameter directly into an Observable, so that we don’t have to use rx.Observable

function getPersonData() {
    return fetch('https://jsonplaceholder.typicode.com/posts/1')
    .then(res => res.json())
}
var source = Rx.Observable.fromEvent(document.body, 'click'); var example = source.concatMap(e => getPersonData()); Subscribe ({next: (value) => {console.log(value); // Return promise instance example.subscribe({next: (value) => {console.log(value); }, error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }});Copy the code

How do you choose the three Operators to use? In fact, it all depends on the situation of use. Here I simply list most of the situations of use

  • ConcatMap is used in situations where it can be determined that the end time of an internal Observable is faster than the sending time of an external Observable, and no parallel processing is expected. It is suitable for a few UI animations or special HTTP request behaviors that need to be completed one at a time.
  • SwitchMap is used in most usage situations as long as the result of the last action.
  • MergeMap is used to process multiple Observables in parallel and is suitable for activities that require parallel processing, such as parallel processing of multiple I/ OS.

It is recommended that beginners use switchMap when not sure which one to choose

When using concatAll or concatMap, please note that the internal Observables must be able to end, and the external Observables cannot send elements much faster than the internal observables. Otherwise there will be memory issues

Today’s summary

Today’s article mainly covers three operators, which should be easily absorbed by readers after reading the last article. The main thing is that you need to think about and pay attention to some details in the use situation.

I wonder if readers have learned anything today? If you have any questions, please leave a message to me, thank you