[Front-end must learn] RxJS, a "mysterious" front-end technology

written in front

RxJS is a JavaScript library that uses observable objects to write asynchronous and event-based programs. It helps developers manage complex asynchronous code more easily and provides many operators to work with observables. RxJS can be used in a variety of applications, including web applications, mobile applications, and desktop applications. However, it is also sometimes considered a "mysterious" technology due to some complex concepts and operators.

Common APIs

RxJS provides very rich methods and APIs. The following are some commonly used methods and APIs in RxJS, as well as detailed examples and usage methods:

Observable creation

  • Observable: An observable object that represents an observable sequence that emits zero or more values ​​and is notified when completed or on error.

  • of: Creates an observable that emits the specified value in sequence.

import {
    
     of } from 'rxjs';

const source = of(1, 2, 3);
source.subscribe(value => console.log(value)); // 输出 1, 2, 3
  • from: Converts an iterable, array-like object, Promise, or similar array-like object into an observable.
import {
    
     from } from 'rxjs';

const source = from([1, 2, 3]);
source.subscribe(value => console.log(value)); // 输出 1, 2, 3
  • fromEvent: Creates an observable object that emits an event of the specified type on the specified DOM element.
import {
    
     fromEvent } from 'rxjs';

const button = document.getElementById('myButton');
const source = fromEvent(button, 'click');
source.subscribe(event => console.log(event)); // 输出点击事件对象
  • interval: Creates an observable that emits an incremented integer every specified interval.
import {
    
     interval } from 'rxjs';

const source = interval(1000);
source.subscribe(value => console.log(value)); // 每秒输出一个递增的整数
  • timer: Creates an observable that emits a value after a specified delay.
import {
    
     timer } from 'rxjs';

const source = timer(1000);
source.subscribe(() => console.log('Hello')); // 一秒后输出 'Hello'
  • range: Creates an Observable that emits integers in the specified range.
import {
    
     range } from 'rxjs';

const source = range(1, 3);
source.subscribe(value => console.log(value)); // 输出 1, 2, 3
  • defer: Creates an Observable that, on subscription, calls a factory function to generate a new Observable.
import {
    
     defer } from 'rxjs';

const source = defer(() => {
    
    
  const randomValue = Math.random();
  return of(randomValue);
});
source.subscribe(value => console.log(value)); // 输出随机数
  • empty: Creates an empty observable that completes immediately.
import {
    
     empty } from 'rxjs';

const source = empty();
source.subscribe({
    
    
  next: () => console.log('Next'),
  complete: () => console.log('Complete')
}); // 立即输出 'Complete'
  • never: Creates an Observable that never emits any value or completes.
import {
    
     never } from 'rxjs';

const source = never();
source.subscribe(() => console.log('Next')); // 不会输出任何内容
  • throwError: Creates an observable that immediately throws an error.
import {
    
     throwError } from 'rxjs';

const source = throwError('Something went wrong');
source.subscribe({
    
    
  next: () => console.log('Next'),
  error: error => console.error(error)
}); // 立即输出错误信息

Observable conversion

  • map: Applies a function to each value emitted by the observable and emits the result.
import {
    
     of } from 'rxjs';
import {
    
     map } from 'rxjs/operators';

const source = of(1, 2, 3);
const mappedSource = source.pipe(map(value => value * 2));
mappedSource.subscribe(value => console.log(value)); // 输出 2, 4, 6
  • filter: Applies a predicate function to each value emitted by the observable, and if it returns true, emits the value.
import {
    
     of } from 'rxjs';
import {
    
     filter } from 'rxjs/operators';

const source = of(1, 2, 3);
const filteredSource = source.pipe(filter(value => value > 1));
filteredSource.subscribe(value => console.log(value)); // 输出 2, 3
  • scan: Applies an accumulator function to each value emitted by the observable and emits the current state of the accumulator as the next value.
import {
    
     of } from 'rxjs';
import {
    
     scan } from 'rxjs/operators';

const source = of(1, 2, 3);
const scannedSource = source.pipe(scan((acc, curr) => acc + curr, 0));
scannedSource.subscribe(value => console.log(value)); // 输出 1, 3, 6
  • buffer: Cache the values ​​emitted by the observable into an array, and emit the cached array as a value under the specified condition.
import {
    
     interval } from 'rxjs';
import {
    
     buffer } from 'rxjs/operators';

const source = interval(1000);
const bufferedSource = source.pipe(buffer(interval(3000)));
bufferedSource.subscribe(value => console.log(value)); // 每三秒输出最近三秒内的值组成的数组
  • concatMap: Maps each value emitted by an observable to a new observable, and chains these observables in turn.
import {
    
     of } from 'rxjs';
import {
    
     concatMap } from 'rxjs/operators';

const source = of('Hello', 'World');
const mappedSource = source.pipe(concatMap(value =>
  of(`${
      
      value} RxJS`).pipe(delay(1000))
));
mappedSource.subscribe(value => console.log(value)); // 输出 "Hello RxJS" 和 "World RxJS",每个值间隔一秒
  • mergeMap: maps each value emitted by an observable to a new observable, and merges these observables into one.
import {
    
     of } from 'rxjs';
import {
    
     mergeMap } from 'rxjs/operators';

const source = of('Hello', 'World');
const mappedSource = source.pipe(mergeMap(value =>
  of(`${
      
      value} RxJS`).pipe(delay(1000))
));
mappedSource.subscribe(value => console.log(value)); // 输出 "Hello RxJS" 和 "World RxJS",每个值间隔一秒
  • switchMap: Map each value emitted by an observable to a new observable, and only subscribe to the latest mapped observable.
import {
    
     of, interval } from 'rxjs';
import {
    
     switchMap } from 'rxjs/operators';

const source = of('Hello', 'World');
const mappedSource = source.pipe(switchMap(() =>
  interval(1000)
));
mappedSource.subscribe(value => console.log(value)); // 每秒输出递增的整数,但只有最新值会被输出
  • concatAll: Combines multiple observables into one and emits all the values ​​they emit in order.
import {
    
     of } from 'rxjs';
import {
    
     concatAll } from 'rxjs/operators';

const source1 = of(1, 2, 3);
const source2 = of(4, 5, 6);
const concatenatedSource = of(source1, source2).pipe(concatAll());
concatenatedSource.subscribe(value => console.log(value)); // 输出 1, 2, 3, 4, 5, 6
  • mergeAll: Combines multiple observables into one and emits all the values ​​they emit at the same time.
import {
    
     of } from 'rxjs';
import {
    
     mergeAll } from 'rxjs/operators';

const source1 = of(1, 2, 3);
const source2 = of(4, 5, 6);
const mergedSource = of(source1, source2).pipe(mergeAll());
mergedSource.subscribe(value => console.log(value)); // 输出所有值,顺序不确定
  • switchAll: Merge multiple observables into one and only subscribe to the latest emitted observable.
import {
    
     of, interval } from 'rxjs';
import {
    
     switchAll } from 'rxjs/operators';

const source1 = interval(1000);
const source2 = interval(2000);
const switchedSource = of(source1, source2).pipe(switchAll());
switchedSource.subscribe(value => console.log(value)); // 只输出最新源发出的值,即每两秒输出一个递增整数

Observable filtering

  • debounceTime: Ignores values ​​emitted by the source for the specified time, and emits the last value if no new values ​​have been emitted by the source during this time.
import {
    
     fromEvent } from 'rxjs';
import {
    
     debounceTime } from 'rxjs/operators';

const input = document.getElementById('myInput');
const source = fromEvent(input, 'input');
const debouncedSource = source.pipe(debounceTime(1000));
debouncedSource.subscribe(event => console.log(event.target.value)); // 在用户停止输入一秒后输出最终输入结果
  • distinctUntilChanged: Only emit this value if the source emits a value different from the previous one.
import {
    
     of } from 'rxjs';
import {
    
     distinctUntilChanged } from 'rxjs/operators';

const source = of(1, 1, 2, 2, 3);
const distinctSource = source.pipe(distinctUntilChanged());
distinctSource.subscribe(value => console.log(value)); // 输出 1, 2, 3(仅在源中前后不同的值才被输出)
  • filter: Applies a predicate function to each value emitted by the observable, and if it returns true, emits the value.
import {
    
     of } from 'rxjs';
import {
    
     filter } from 'rxjs/operators';

const source = of(1, 2, 3);
const filteredSource = source.pipe(filter(value => value > 1));
filteredSource.subscribe(value => console.log(value)); // 输出 2, 3(仅源中大于一的值被输出)
  • first: Emit the value only when the source emits the first value, which completes immediately. Used to emit the first value of the Observable, then complete. If the observable is null, a default value is emitted or an error is thrown.
import {
    
     of } from 'rxjs';
import {
    
     first } from 'rxjs/operators';

const source = of(1, 2, 3);
const firstValue = source.pipe(first());
firstValue.subscribe(value => console.log(value)); // 输出 1

In the example above, we created an observable containing 1, 2, 3 and used the first method to select the first value. Therefore, we get the value 1 from the output of the first method.

Here's another example of the first method, where we provide a predicate function to select the first value to emit:

import {
    
     of } from 'rxjs';
import {
    
     first } from 'rxjs/operators';

const source = of(1, 2, 3);
const firstEvenValue = source.pipe(first(value => value % 2 === 0));
firstEvenValue.subscribe(value => console.log(value)); // 输出 2

In the example above, we created an observable containing 1, 2, 3 and used the first method to select the first even value. Therefore, we get the value 2 from the output of the first method.

If the observable is null, the first method emits a default value or throws an error. Here's an example of the first method emitting a default value:

import {
    
     EMPTY } from 'rxjs';
import {
    
     first } from 'rxjs/operators';

const source = EMPTY;
const defaultValue = source.pipe(first(undefined, 'Default Value'));
defaultValue.subscribe(value => console.log(value)); // 输出 'Default Value'

In the above example, we created an empty observable and used the first method to select the first value. Since the observable is null, the first method emits the default value 'Default Value'.

If no default value is provided, the first method throws an error. Here is an example where the first method throws an error:

import {
    
     EMPTY } from 'rxjs';
import {
    
     first } from 'rxjs/operators';

const source = EMPTY;
const noDefaultValue = source.pipe(first());
noDefaultValue.subscribe(value => console.log(value), error => console.log(error)); // 输出错误信息

In the above example, we created an empty observable and used the first method to select the first value. The first method throws an error because the observable is empty and no default value was provided.

Other APIs

  • Observable: An observable object that represents an observable sequence that emits zero or more values ​​and is notified when completed or on error.

  • of: Creates an observable that emits the specified value in sequence.

  • from: Converts an iterable, array-like object, Promise, or similar array-like object into an observable.

  • fromEvent: Creates an observable object that emits an event of the specified type on the specified DOM element.

  • interval: Creates an observable that emits an incremented integer every specified interval.

  • timer: Creates an observable that emits a value after a specified delay.

  • range: Creates an Observable that emits integers in the specified range.

  • defer: Creates an Observable that, on subscription, calls a factory function to generate a new Observable.

  • empty: Creates an empty observable that completes immediately.

  • never: Creates an Observable that never emits any value or completes.

  • throwError: Creates an observable that immediately throws an error.

  • map: Applies a function to each value emitted by the observable and emits the result.

  • filter: Applies a predicate function to each value emitted by the observable, and if it returns true, emits the value.

  • scan: Applies an accumulator function to each value emitted by the observable and emits the current state of the accumulator as the next value.

  • reduce: Applies an accumulator function to each value emitted by the observable and emits the final state of the accumulator when complete.

  • take: Fetches the specified number of values ​​from the observable and is done.

  • takeUntil: Takes a value from an observable until another observable emits a value.

  • mergeMap: maps each value emitted by an observable to a new observable, and merges these observables into one.

  • switchMap: maps each value emitted by an observable to a new observable, and replaces the previous observable with the latest observable.

  • concatMap: maps each value emitted by an observable to a new observable, and waits for the previous observable to complete before subscribing to the next observable.

  • debounceTime: Ignores duplicate values ​​that occur consecutively within the specified time and emits only the last value.

  • distinctUntilChanged: Ignore consecutively repeated values ​​and emit only non-repeated values.

  • tap: Perform some side-effect operation on every value emitted by the observable without changing them.

  • catchError: Catch errors in an observable and either return another observable or throw another error.

The above are common methods and APIs of RxJS, which can help developers manage complex asynchronous code more easily.

Advanced

Composition of Observables

  • concat: Connect multiple observable objects in sequence, and emit the value of each observable object in turn.
import {
    
     of, concat } from 'rxjs';

const source1 = of(1, 2, 3);
const source2 = of(4, 5, 6);
const concatenatedSource = concat(source1, source2);
concatenatedSource.subscribe(value => console.log(value)); // 输出 1, 2, 3, 4, 5, 6
  • merge: Merges multiple observables into one, emitting the values ​​of all observables at the same time.
import {
    
     of, merge } from 'rxjs';

const source1 = of(1, 2, 3);
const source2 = of(4, 5, 6);
const mergedSource = merge(source1, source2);
mergedSource.subscribe(value => console.log(value)); // 输出所有值,顺序不确定
  • combineLatest: Combines multiple observables into one, emitting a value only if all observables emit at least one value.
import {
    
     combineLatest, interval } from 'rxjs';

const source1 = interval(1000);
const source2 = interval(2000);
const combinedSource = combineLatest(source1, source2);
combinedSource.subscribe(value => console.log(value)); // 每秒输出一个数组,包含两个可观察对象最新的值
  • zip: Merges multiple observables into one, emitting a value only when all observables emit a value.
import {
    
     zip, interval } from 'rxjs';

const source1 = interval(1000);
const source2 = interval(2000);
const zippedSource = zip(source1, source2);
zippedSource.subscribe(value => console.log(value)); // 每两秒输出一个数组,包含两个可观察对象最新的值
  • startWith: Emits the specified initial value before the Observable emits a value.
import {
    
     of } from 'rxjs';
import {
    
     startWith } from 'rxjs/operators';

const source = of(1, 2, 3);
const startWithSource = source.pipe(startWith(0));
startWithSource.subscribe(value => console.log(value)); // 输出 0, 1, 2, 3
  • withLatestFrom: Combines an Observable with another or more Observables into one that emits a value only when the first Observable emits a value.
import {
    
     interval } from 'rxjs';
import {
    
     withLatestFrom } from 'rxjs/operators';

const source1 = interval(1000);
const source2 = interval(500);
const withLatestFromSource = source1.pipe(withLatestFrom(source2));
withLatestFromSource.subscribe(value => console.log(value)); // 每秒输出一个数组,包含 source1 最新的值和 source2 最新的值

Observable filtering

  • debounceTime: During the specified interval, ignore values ​​emitted by the Observable and emit the last value at the end of the interval.
import {
    
     fromEvent } from 'rxjs';
import {
    
     debounceTime } from 'rxjs/operators';

const input = document.getElementById('myInput');
const source = fromEvent(input, 'input');
const debouncedSource = source.pipe(debounceTime(1000));
debouncedSource.subscribe(event => console.log(event.target.value)); // 在用户停止输入一秒钟后输出最后一次输入的值
  • distinctUntilChanged: Ignore consecutively repeated values ​​and emit only non-repeated values.
import {
    
     of } from 'rxjs';
import {
    
     distinctUntilChanged } from 'rxjs/operators';

const source = of(1, 1, 2, 2, 3, 3);
const distinctSource = source.pipe(distinctUntilChanged());
distinctSource.subscribe(value => console.log(value)); // 输出 1, 2, 3
  • filter: Applies a predicate function to each value emitted by the observable, and if it returns true, emits the value.
import {
    
     of } from 'rxjs';
import {
    
     filter } from 'rxjs/operators';

const source = of(1, 2, 3);
const filteredSource = source.pipe(filter(value => value > 1));
filteredSource.subscribe(value => console.log(value)); // 输出 2, 3
  • take: emits only the specified number of values, then completes.
import {
    
     interval } from 'rxjs';
import {
    
     take } from 'rxjs/operators';

const source = interval(1000);
const takenSource = source.pipe(take(3));
takenSource.subscribe(value => console.log(value)); // 输出前三个递增的整数
  • takeUntil: only emits values ​​before another observable starts emitting values ​​and then finishes.
import {
    
     interval, timer } from 'rxjs';
import {
    
     takeUntil } from 'rxjs/operators';

const source = interval(1000);
const timer$ = timer(5000);
const takenSource = source.pipe(takeUntil(timer$));
takenSource.subscribe(value => console.log(value)); // 输出前五个递增的整数,然后完成

Observable error handling

  • catchError: Catch the error and return another observable or throw an error.
import {
    
     of } from 'rxjs';
import {
    
     catchError } from 'rxjs/operators';

const source = of('Hello', 'World', new Error('Oops!'));
source.pipe(
  map(value => value.toUpperCase()),
  catchError(error => {
    
    
    console.error(error.message);
    return of(null);
  })
).subscribe(value => console.log(value)); // 输出 HELLO, WORLD, null,并打印错误信息
  • retry: Retry the value emitted by the observable on error. Used to automatically retry when an error occurs on an observable. It attempts to resubscribe to the observable, retrying the specified number of times each time an error occurs.
import {
    
     interval } from 'rxjs';
import {
    
     retry } from 'rxjs/operators';

const source = interval(1000).pipe(
  map(value => {
    
    
    if (value === 3) {
    
    
      throw new Error('Oops!');
    }
    return value;
  })
);
const retriedSource = source.pipe(retry(2));
retriedSource.subscribe({
    
    
  next: value => console.log(value),
  error: error => console.error(error)
});

If we set the parameter of the retry method to -1, the observable will keep retrying until it succeeds.

—————————— [End of text] ——————————

Front-end learning exchange group, if you want to come in face-to-face, you can join the group: 832485817 , 685486827 ;
Front-end top learning exchange group (1) Front-end top learning exchange group (2)

Written at the end: convention is better than configuration - the principle of simplicity in software development

—————————— 【End】 ——————————

My:
Personal website: https://neveryu.github.io/neveryu/
Github: https://github.com/Neveryu
Wechat: miracle421354532

For more learning resources, please pay attention to my WeChat... okay?

Guess you like

Origin blog.csdn.net/csdn_yudong/article/details/132177273