浅析RXJS

150 阅读8分钟

浅析RXJS

最近在开发过程中涉及到了rxjs这个工具,因此笔者决定结合最近的使用心得,浅析rxjs的基本概念,常用api以及使用方式和场景。

简介

RxJS(Reactive Extensions for JavaScript)是Reactive Extensions的JavaScript版本,是一个用于处理异步数据流的库,它基于观察者模式和迭代器模式。RxJS 在 Angular 中被广泛使用,同时也能够在其他 JavaScript 和 TypeScript 项目中使用。

基本概念
响应式编程

响应式编程基于数据流(例如用户输入、网络请求的响应、定时器等)和事件驱动,它允许开发者通过使用 Observable(可观察对象)、Observer(观察者) 和 Subscription(订阅者) 等核心概念,以声明性的方式处理异步数据流和事件,从而简化对复杂数据和状态变化的管理。

核心概念
  1. Observable(可观察对象) RxJS 使用 Observable 作为核心概念,代表可以发出多个值的异步数据流。Observable 可以是用户输入、HTTP 请求、定时器等。它可以发出一系列值或事件,并且可以被订阅。Observable 可以看作是一个函数,它接收一个 Observer(观察者) 并返回一个 Subscription(订阅者)。

可以使用 RxJS 库中的 of、from、interval 等方法来创建 Observable。

import { of, from, interval } from 'rxjs';

// 使用 of 创建一个 Observable
// of 用于从一组值创建一个 Observable。它可以接受任意数量的参数,并将这些参数作为单独的值发出。
const observableOf = of(1, 2, 3);

// 使用 from 创建一个 Observable
// from 用于将其他类型的对象(如数组、Promise、Iterable、Observable 等)转换为 Observable。
// 使用 from 来处理异步数据源或将集合转换为 Observable,适合于需要逐个发出值的场景。
const observableFrom = from([1, 2, 3]);

// 使用 interval 创建一个每隔1秒发出一个递增数字的 Observable
const observableInterval = interval(1000);

Observable 有三种状态:未订阅、已订阅、已完成/已出错。

  • 在未订阅状态下,Observable 不执行任何操作;

  • 在已订阅状态下,它开始执行逻辑,发出数据,可以接收来自 Observer 的观察和处理;

  • 在已完成或已出错状态下,Observable 不再发出新的值,并会通知 Observer。

    • 当 Observable 发出所有值后,它会进入已完成状态,调用 Observer 的 complete 方法,表示数据流已结束,用户可以释放资源。
    • 如果在 Observable 的执行过程中发生错误,Observable 会进入已出错状态。此时调用 Observer 的 error 方法,传递错误信息。

2.Observer:

  • Observer 是对 Observable 的订阅者,接收 Observable 发出的数据。Observer 定义了如何处理接收到的数据。
  • Observer 有三个主要的方法:next: 当 Observable 发出一个新的值时调用。 error: 当 Observable 发生错误时调用。 complete: 当 Observable 完成时调用。
// 定义一个 Observer
const observer = {
 next: (value) => console.log('Next value:', value),
 error: (err) => console.error('Error:', err),
 complete: () => console.log('Completed')
}
  1. Subscription(订阅)

Subscription 是对 Observable 的一个订阅实例。通过订阅,Observer 可以接收来自 Observable 的数据流,并可以在需要时取消订阅,以避免内存泄漏。

下面是一个完整的示例,展示了如何创建一个 Observable,定义一个 Observer,并进行订阅。


组件类 (app.component.ts)
import { Component, OnInit, OnDestroy } from '@angular/core';
import { of, interval } from 'rxjs';
import { Subscription } from 'rxjs';

@Component({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
 private subscription: Subscription;

ngOnInit() {
 // 创建一个 Observable
 const observable = interval(1000); // 每隔1秒发出一个递增的数字


// 定义一个 Observer,表示如何处理observable对象
const observer = {
 next: (value) => console.log('Next value:', value),
 error: (err) => console.error('Error:', err),
 complete: () => console.log('Completed')
};

// 订阅 Observable。使用 subscribe 方法将 Observer 与 Observable 连接起来。每当 Observable 发出新的值时,Observer 的 next 方法会被调用。
this.subscription = observable.subscribe(observer);

```

}

ngOnDestroy() {
 // 取消订阅
 if (this.subscription) {
 this.subscription.unsubscribe();
 }
 }
}
  1. Operators 操作符

用于处理和转换 Observable 数据的方法。 常见的操作符包括 map、filter、flatMap、mergeMap、switchMap 等。

  • map:用于对流中的数据进行转换。将 Observable 发出的每个值应用一个函数,并返回一个新的 Observable。

  • filter:根据指定条件过滤 Observable 发出的值,只发出满足条件的值。

  • flatMap或 mergeMap:将每个值映射到一个新的 Observable,并将这些 Observables 合并成一个单一的 Observable。

  • switchMap:将每个值映射到一个新的 Observable,取消之前的 Observable,仅保留最近的 Observable 的值。合处理高频率的异步操作,如搜索框输入。

  • debounceTime:在指定的时间段内,如果没有发出新值,则发出最后一个值。常用于处理用户输入事件以减少频繁的事件触发。

    import { fromEvent } from 'rxjs';
    import { debounceTime, map } from 'rxjs/operators';
    
    const input$ = fromEvent(document.getElementById('input'), 'input');
    const result$ = input$.pipe(
      debounceTime(300),
      map(event => event.target.value)
    );
    
    result$.subscribe(value => console.log(value));
    
  1. Subject

Subject是一种特殊的 Observable,可以同时作为 Observer 和 Observable,适合用于多播(multicast)场景,允许多个订阅者共享同一个数据流。 常见的 Subject 类型包括 Subject、BehaviorSubject、ReplaySubject 和 AsyncSubject。

  • Subject:基本的多播 Observable,可以发送数据给多个订阅者。
  • BehaviorSubject:带有初始值的 Subject,始终发出最新值。
  • ReplaySubject:缓存过去 n 个值并发给新订阅者。
  • AsyncSubject:只在完成时发出最后一个值。
  1. Schedulers

Schedulers用于控制 Observable 的执行上下文和时间。通过不同的 Scheduler,开发者可以在合适的时机执行任务,以优化应用的性能和响应性。 常见的 Scheduler 包括 queueScheduler、asapScheduler、asyncScheduler 等。

响应式编程和promise的区别

两者都可以用来处理异步操作。

Promise 是一个表示未来可能完成或失败的异步操作的对象。它只能处理一个异步事件,并且能够在未来某个时刻返回一个值或错误,因此应用场景多用于处理单一的异步操作,如单个 API 请求、文件读取等;

响应式编程则强调对数据流的订阅和反应,适用于处理多个异步事件和连续的数据流,适合需要长时间监听的场景,例如处理 WebSocket 消息、实时数据更新,用户输入等。

常见的应用场景
与 Angular 的集成

RxJS 与 Angular 框架紧密集成,成为 Angular 中处理异步操作的标准方式。Angular 的 HTTP 客户端、路由等都使用了 Observable。

  1. HTTP 请求:

使用 HttpClient 进行 HTTP 请求,并处理响应数据。

 this.http.get('https://api.example.com/data')
 // pipe 是 RxJS 中的一个方法,用于将多个操作符组合在一起,处理 Observable 的数据流
   .pipe(
 // 这是一个 RxJS 的 map 操作符,用于转换 Observable 发出的数据。
 // response.data:假设响应对象中有一个 data 属性,map 操作符将提取这个属性的值,并将其作为新的 Observable 发出。
     map(response => response.data)
   )
 // .subscribe(...):这是对 Observable 的订阅。订阅是开始接收数据流的方式。
   .subscribe(data => {
     console.log(data);
   });
  1. 路由:

在 Angular 中,路由使用 Observable 提供了强大的功能,使得开发者能够轻松处理路由变化、获取路由参数和守卫等。

// 1.路由参数 使用 `ActivatedRoute` 服务获取当前激活的路由信息。
this.activatedRoute.params.subscribe(params => {
      this.itemId = params['id'];
});

// 2.查询参数 使用 ActivatedRoute 的 queryParams 属性可以获取 URL 中的查询参数。
this.activatedRoute.queryParams.subscribe(queryParams => {
  const filter = queryParams['filter'];
});

// 3.路由事件 使用Router服务订阅路由事件,允许开发者在路由变化时执行相应的逻辑
this.router.events.subscribe(event => {
      if (event instanceof NavigationStart) {
        console.log('导航开始');
      } else if (event instanceof NavigationEnd) {
        console.log('导航结束');
      }
});

// 4.路由守卫(如 CanActivate 和 CanDeactivate)可以使用 Observable 来控制导航
// canActivate:守卫方法,用于决定是否允许导航到特定的路由。
// route: ActivatedRouteSnapshot:表示当前激活的路由快照,可以获取路由参数、查询参数等信息。
// state: RouterStateSnapshot:表示当前路由状态的快照,包含了整个路由树的信息。
 canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> {
    const isLoggedIn = false; // 示例:根据实际认证逻辑决定
    if (!isLoggedIn) {
 // 如果用户未登录,则重定向到登录页面,同时返回一个 Observable,发出 false,表示导航被禁止。
      this.router.navigate(['/login']);
      return of(false);
    }
    return of(true);
  }

表单验证:

实时验证表单输入,并根据输入变化更新 UI。

 const input = document.getElementById('input');
 const form = document.getElementById('form');

 const input$ = fromEvent(input, 'input').pipe(
   map(event => event.target.value),
   debounceTime(300)
 );

 const form$ = fromEvent(form, 'submit');

 const search$ = merge(input$, form$).pipe(
   switchMap(query => this.searchService.search(query))
 );

 search$.subscribe(results => {
   console.log(results);
 });
WebSocket 通信:

处理 WebSocket 消息流

 const subject = webSocket('ws://example.com/socket');

 subject.subscribe(
   msg => console.log('message received: ' + msg),
   err => console.error(err),
   () => console.log('complete')
 );

 subject.next({ op: 'ping' });
定时任务:

定期执行任务,如轮询服务器。

 const source = interval(5000); // 每5秒触发一次
 const example = source.pipe(
   switchMap(() => this.http.get('https://api.example.com/data'))
 );

 example.subscribe(data => {
   console.log(data);
 });
事件处理:

处理 DOM 事件或其他事件流。

 const click$ = fromEvent(document, 'click').pipe(
   throttleTime(500),
   map(event => event.clientX)
 );

 click$.subscribe(position => {
   console.log('Clicked at position:', position);
 });
应用场景
  • 在用户界面中处理用户输入、事件和状态变化,确保 UI 始终与数据保持同步。

  • 处理来自不同来源的数据流,如 WebSocket、HTTP 请求、文件读取等。

  • 聊天应用、实时数据监控和在线游戏等场景。

总结

RxJS 是响应式编程的强大实现,通过 Observable 和丰富的操作符,提供了处理异步数据流和事件的能力。它使得开发者能够以声明性的方式处理复杂的数据流,简化异步操作的管理。因此,RxJS 成为现代 JavaScript 应用程序,尤其是使用 Angular 的应用程序中,处理异步操作的核心工具。