RxJS基本概念入门

Angular开发不得不提另外一大神器RxJS,它是一个非常强大的工具,可以帮助您在JavaScript中更好地处理异步编程。以下是关于RxJS的一些基本知识和资源,供您学习参考。

什么是RxJS?

RxJS代表响应式扩展JavaScript。它是一个用于处理事件流和异步数据流的库,可以将这些流组合起来以产生更复杂的结果。

RxJS核心概念

  • Observable: 表示一个可调用的未来值或事件的集合。
  • Observer: 一个回调函数的集合,用于处理由Observable发出的值,包括 next、error 和 complete。
  • Subscription: 代表Observable的执行,主要用于取消 Observable 的执行。
  • Operators: 纯函数,允许以声明性方式纯变换 Observable 中的值,比如 map()、filter()、concat()等等。
  • Subject: 相当于 EventEmitter,并且是将值多路推送给多个观察者的唯一方法。

Observable

在RxJS中,一个Observable代表了一个可以推送0个或者多个值(value)的集合,相当于流(stream)。有几种方法可以创建和转化Observable。一旦创建Observable,就可以使用各种附加操作符来轻松的创建、组合和修改Observables。

以下是一些关于Observable的核心概念:

创建Observable

您可以使用of, from, interval, timer, ajax, create等操作符来创建Observable,具体取决于你需要使用的数据源类型。例如:

// 从一个静态值创建一个Observable
const myObservable = of('hello', 'world');

// 从数组中创建一个Observable
const myArrayObservable = from([1, 2, 3, 4, 5]);

// 创建一个每秒发送递增整数的Observable
const intervalObservable = interval(1000);

// 延迟三秒后发射单个值的Observable
const timerObservable = timer(3000, 1000);

订阅Observable

Observables是惰性的,只有当您订阅它们时,它们才会开始推送值。可以使用.subscribe()方法来订阅Observable,然后处理这些发出的值。

myObservable.subscribe(value => console.log(value));

解除订阅

一旦订阅Observable,您需要决定何时解除订阅。使用 .subscribe()返回的订阅对象,可以通过调用 .unsubscribe() 来解除订阅。

const subscription = myArrayObservable.subscribe(value => console.log(value));

// 在3秒后取消订阅
setTimeout(() => {
    
    
  subscription.unsubscribe();
}, 3000);

处理错误

当Observable发出错误消息时,您需要相应地处理这些错误。您可以使用 catchError 操作符来捕获和处理错误。

myObservable.pipe(
    catchError(error => of(`An error occurred: ${
      
      error}`))
  ).subscribe(value => console.log(value));

连接多个Observables

您可以使用 connectable operators 来同时订阅多个Observables并将它们连接在一起。类似的操作符有 merge, concat, combineLatest, forkJoin等等。

const observable1 = of('hello');
const observable2 = of('world');

const combinedObservable = combineLatest([observable1, observable2]);

combinedObservable.subscribe(value => console.log(value)); // ["hello", "world"]

Observer

在RxJS中,Observer代表了一个观察者对象,它可以处理Observable发出的0个或多个值,并能够对Observable产生的错误和完成事件做出反应。

以下是一些关于Observer的核心概念:

创建Observer

您可以使用nexterrorcomplete三个属性来创建一个Observer。

const myObserver = {
    
    
  next: value => console.log(value),
  error: err => console.error(err),
  complete: () => console.log('Observable completed')
};

订阅Observable

为了订阅Observable,您需要将Observer作为参数传递给Observable上的 .subscribe() 方法。

myObservable.subscribe(myObserver);

当Observable发出新的值时,它会调用Observer的 next 属性来推送这个值。同样地,当Observable发出错误消息时,它会调用Observer的 error 属性,并调用 complete 属性来标记Observable已经完成。

const myObservable = of('hello', 'world');

// 订阅Observable
myObservable.subscribe({
    
    
  next: value => console.log(value),
  error: err => console.error(err),
  complete: () => console.log('Observable completed')
});

处理错误

您可以选择仅仅定义 nexterror 的Observer来处理Observable发出的错误消息。

const myObserver = {
    
    
  next: value => console.log(value),
  error: err => console.error(err)
};

myObservable.subscribe(myObserver);

处理完成事件

您可以选择仅定义 nextcomplete 的 Observer 来处理 Observable 完成时的事件。

const myObserver = {
    
    
  next: value => console.log(value),
  complete: () => console.log('Observable completed')
};

myObservable.subscribe(myObserver);

Subscription

在 RxJS 中,Subscription(订阅)是另一个重要的概念。当我们使用 Observable 订阅数据源时,它会返回一个 Subscription 对象,该对象用于断开与数据源的连接。

订阅

为了订阅一个 Observable,我们必须调用 Observable 的 subscribe() 函数。这个函数将返回一个 Subscription 对象。

const subscription = myObservable.subscribe(value => {
    
    
    console.log('Received value:', value);
});

取消订阅

如果我们想取消一个订阅,我们可以调用 Subscription 对象上的 unsubscribe() 方法。

subscription.unsubscribe();

在调用 unsubscribe() 后,我们将不再接收到来自 Observable 的任何响应。

订阅后的资源清理

另外,Subscription 还提供了 add() 和 remove() 方法,这两个方法都接受一个子订阅(子 Subscription)。

它们可以被用于在父级 Subscription 上注册和取消注册订阅,在父级 Subscription 取消时,所有已经注册的子 Subscription 都会自动取消订阅。

const parentSubscription = new Subscription();
const childSubscription = myObservable.subscribe(value => {
    
    
    console.log('Received value:', value);
});

parentSubscription.add(childSubscription); // 注册子订阅

// 父 Subscription 将取消所有的子 Subscription。
parentSubscription.unsubscribe(); 

Operator

在 RxJS 中,Operator 是一种函数,它接收 Observable 的输入数据并输出变换后的 Observable。

可以将一个或多个 Operator 组成一条管道(pipe),通过这条管道将数据逐步地传递,并逐步地将其转换成我们需要的形式。

管道

管道是由一系列 Operator 组成的链式调用。为了创建一个管道,我们需要使用 Observable 上的 pipe() 方法,并将 Operator 作为参数传入其中。

import {
    
     map, filter } from 'rxjs/operators';

const myObservable = of(1, 2, 3, 4, 5);

myObservable.pipe(
    filter(value => value % 2 === 0), // 过滤偶数
    map(value => value * 10) // 将值乘以 10
).subscribe(result => {
    
    
    console.log('Received result:', result);
});

纯函数

在 RxJS 中,Operator 应该是纯函数——即根据特定的输入总是产生相同的输出。这使得它们非常容易测试、复用和组合。

常见的 Operator

RxJS 附带了许多内置的 Operator,例如 map、filter、reduce、zip、merge、concat 等等。此外,还有很多社区开发的 Operator 库,例如 ngRx、ngrx-data、ngx-query 等库,这些库提供了大量的 Operator,可用于简化不同场景下的应用程序开发。

Subject

在 RxJS 中,Subject 是一种特殊的 Observable。与正常的 Observable 不同之处在于,Subject 可以像事件一样多次发出值,并且它们会同时将值发送给所有正在监听该 Subject 的观察者。

Subject 有以下两个主要的变体:

BehaviorSubject

BehaviorSubject 需要一个初始值,并且每当订阅时都会将最新值发送给观察者。

import {
    
     BehaviorSubject } from 'rxjs';

const subject = new BehaviorSubject('Initial value');

subject.subscribe({
    
    
  next: (v) => console.log(`ObserverA: ${
      
      v}`)
});

subject.next('New value');

subject.subscribe({
    
    
  next: (v) => console.log(`ObserverB: ${
      
      v}`)
});

// output:
// ObserverA: Initial value
// ObserverA: New value
// ObserverB: New value

在上面的示例中,我们创建了一个 BehaviorSubject,并在初始时传递了一个值。当我向 BehaviorSubject 发送新值时,它会将此值发送给所有已订阅它的观察者。注意,即使是在第二个订阅之后发送的新值,也会被发送到新的观察者 ObserverB。

ReplaySubject

ReplaySubject 在创建时不需要初始值,但可以缓存最近发出的 n 个值,而新的订阅者将立即收到这些值的回放。

import {
    
     ReplaySubject } from 'rxjs';

const subject = new ReplaySubject(2); // 缓存最近2个值

subject.subscribe({
    
    
  next: (v) => console.log(`ObserverA: ${
      
      v}`)
});

subject.next('Value 1');
subject.next('Value 2');
subject.next('Value 3');

subject.subscribe({
    
    
  next: (v) => console.log(`ObserverB: ${
      
      v}`)
});

// output:
// ObserverA: Value 1
// ObserverA: Value 2
// ObserverA: Value 3
// ObserverB: Value 2
// ObserverB: Value 3

在上面的示例中,我们创建了一个 ReplaySubject,并在构造函数中传递了参数 2。这表示该主体将缓存最近的2个值。

当我向回放主题发送3个值时,所有已订阅它的观察者都会收到这些值。

注意,新的观察者 ObserverB 会立即接收到缓存的值。在本例中,缓存了2个值并且 ReplaySubject 已经发出过3个值,但新的观察者仍然能够看到最后两个值(Value 2 和 Value 3)。

AsyncSubject

AsyncSubject 只会在完成时发出最后一个值(即,当 Subject 的 complete() 方法被调用时)。

import {
    
     AsyncSubject } from 'rxjs';

const subject = new AsyncSubject();

subject.subscribe({
    
    
  next: (v) => console.log(`ObserverA: ${
      
      v}`)
});

subject.next('Value 1');
subject.next('Value 2');

subject.subscribe({
    
    
  next: (v) => console.log(`ObserverB: ${
      
      v}`)
});

subject.next('Value 3');
subject.complete();

// output:
// ObserverA: Value 3
// ObserverB: Value 3

在上面的示例中,我们创建了一个 AsyncSubject,并订阅了两个观察者。然后发送三个值并调用 complete() 方法。在此之后,AsyncSubject 在其内部缓存了最后一个值和完成通知,并将这个值发送给所有已订阅的观察者 ObserverA 和 ObserverB。

由于 AsyncSubject 只有在调用 complete() 方法时才会发出值,因此如果没有发出值就调用了 complete() 方法,则不会有任何发射。

Subject 是非常有用的 Observable 变体,它可以处理同步和异步代码的各种情况,以及更复杂的 RxJS 操作符的某些用例。

BehaviorSubject

BehaviorSubject 与上面的 Subject 相似,但是它会在订阅时发出最后一个值并且在每次更新时重新发送这个值。

import {
    
     BehaviorSubject } from 'rxjs';

const subject = new BehaviorSubject('initial value');

subject.subscribe({
    
    
  next: (v) => console.log(`ObserverA: ${
      
      v}`)
});

subject.next('Value 1');
subject.next('Value 2');

subject.subscribe({
    
    
  next: (v) => console.log(`ObserverB: ${
      
      v}`)
});

subject.next('Value 3');

// output:
// ObserverA: initial value
// ObserverA: Value 1
// ObserverA: Value 2
// ObserverB: Value 2
// ObserverA: Value 3
// ObserverB: Value 3

在上面的示例中,我们创建了一个 BehaviorSubject,并传递了初始值 initial value。我们订阅了两个观察者(ObserverA 和 ObserverB),并发送了三个值。注意,当第二个观察者订阅时,它会立即收到最新的值 Value 2,并且在之后每发送一个新值时它也会一同更新。

对于 BehaviorSubject,有一个重要的注意点:BehaviorSubject 要求在创建时提供一个初始值,因为它可能需要在没有其他值时发出它。

BehaviorSubject 通常用于表示 “状态” 或 “数据源”,并且多个组件可以共享相同的 BehaviorSubject 实例来获取和更新该数据。它还可用于缓存最新的值。

RxJS实践教程

以下是一些不错的RxJS实践教程,供您学习参考:

希望这些资源能够帮助您快速入门RxJS,并在JavaScript编程中更轻松地处理异步流。

猜你喜欢

转载自blog.csdn.net/lin5165352/article/details/132619475