RxJS入门

  1. 简介
    1. RxJS英文全称是Reactive Extensions for JavaScript,响应式扩展的js;是一个使用可观察序列编写异步和事件程序的库。
    2. ReactiveX将观察者模式、迭代器模式、函数式编程和集合相结合,以便满足对管理事件序列的一种理想方式。
    3. RxJS解决异步事件管理的基本概念如下:
      • Observable:可观察者,表示未来有值或事件的可调用集合的概念,其实就是定义一个懒加载的函数对象,可以稍后被调用也可以不被调用,在调用的时候将会生产数据。
      • Observer:观察者,它是一个回调的集合,所以它监听了Observable传递的值并且可以做处理,其实就是用于消费Observable生产的数据。
      • Subscription:订阅或者消费,表示Observable的执行,当订阅了一个Observable后将返回Subscription对象,此对象主要用于取消订阅。
      • Operators:操作函数,可以使用函数式编程风格来处理具有map、filter、concat、reduce等操作的集合。(回头再补充)
      • Subject:主题,相当于事件发送者(EventEmitter),也是将一个值或者事件传播到多个Observable的唯一方式(其实就是Observable的多播实现方式)
      • Schedulers:调度者,是控制并发的集中调度程序,当发生像setTimeout、requestAnimationFrame等其他计算的时候允许我们进行协调。
  2. Observable
    1. 可观察者是多个值的惰性Push集合,常见的Pull对象有Function、Iterator,Promise是单值的Push
      • 惰性代表是懒加载,定义好的Observable并不会被立即执行,如下:
      • 查看代码
        import { Observable } from 'rxjs';
        
        const observable = new Observable(subscriber => {
          subscriber.next(1);
          subscriber.next(2);
          setTimeout(() => {
            subscriber.next(3);
            subscriber.complete();
          }, 1000);
        });
      • 只有当订阅了此Observable才会被调用,如下:
      • 查看代码
        import { Observable } from 'rxjs';
        
        const observable = new Observable(subscriber => {
          subscriber.next(1);
          subscriber.next(2);
          setTimeout(() => {
            subscriber.next(3);
            subscriber.complete();
          }, 1000);
        });
        
        console.log('just before subscribe');
        observable.subscribe({
          next(x) { console.log('got value ' + x); },
          error(err) { console.error('something wrong occurred: ' + err); },
          complete() { console.log('done'); }
        });
        console.log('just after subscribe');
      • 在控制台上执行如下:
      • 查看代码
        just before subscribe
        got value 1
        got value 2
        just after subscribe
        got value 3
        done
    2. Pull和Push是两种不同的协议,用于描述生产者与消费者如何通讯的方式,说白了,Pull就是消费者主动去要数据,Push就是消费者被动接收数据(类似于等待生产者告诉你有数据了你才会去消费)。
      • 在Pull系统中,消费者将确定什么时候获取生产者的数据,生产者本身不知道何时将会被数据给消费者,像Function这种,只有当被调用的时候才会产生数据,又或者像Iterator,只有调用其next方法的时候才会产生数据
      • 在Push系统中,生产者决定何时向消费者发送数据,消费者不知道何时会收到该数据,一般都是使用回调的方式来实现被动消费
    3. Observable是同步执行的,只不过它也支持异步(比如说Http请求或者定时器)
      • 同步调用普通方法如下:
      • 查看代码
        function foo() {
          console.log('Hello');
          return 42;
        }
        
        console.log('before');
        console.log(foo.call());
        console.log('after');
      • 输出结果如下:
      • 查看代码
        "before"
        "Hello"
        42
        "after"
      • 使用Observable执行如下:
      • 查看代码
        const foo = new Observable(subscriber => {
          console.log('Hello');
          subscriber.next(42);
        });
        console.log('before');
        foo.subscribe(x => {
          console.log(x);
        });
        console.log('after');
      • 输出结果如下:
      • 查看代码
        "before"
        "Hello"
        42
        "after"
    4. Observables 可以随着时间的推移“返回”多个值,但是Function不可能进行多次return
      • 查看代码
        import { Observable } from 'rxjs';
        
        const foo = new Observable(subscriber => {
          console.log('Hello');
          subscriber.next(42);
          subscriber.next(100); // "return" another value
          subscriber.next(200); // "return" yet another
        });
        
        console.log('before');
        foo.subscribe(x => {
          console.log(x);
        });
        console.log('after');
      • 输出结果如下:
      • 查看代码
        "before"
        "Hello"
        42
        100
        200
        "after"
         
    5. 创建
      • 使用Subscribe函数作为参数的方式创建Observable,如下(其他方式在Observer介绍中说明)创建了一个Observable对象,在被订阅后将每秒发送一个hi的字符给消费者:
      • 查看代码
        import { Observable } from 'rxjs';
        
        const observable = new Observable(function subscribe(subscriber) {
          const id = setInterval(() => {
            subscriber.next('hi')
          }, 1000);
        });
      • Observable可以使用new的方式创建,最常见的方式应该是使用of、from、interval、httpClient请求等方式
    6. 订阅
      • 可以使用如下方式对上面创建的Observable进行订阅:
      • 查看代码
        observable.subscribe(x => console.log(x));
      • 订阅一个Observable对象就像调用一个函数,产生的数据将会被回传到回调函数中(Subscribe函数)
    7. 执行
      • 创建描述中的Subscribe其实代表的就是Observable的execution
      • Execution可以传递三种类型的值:
        • next:发送数值、字符串、对象等。
        • error:发送JavaScript的错误或者异常。
        • complete:不发送值,仅作为正常完成后的自定义处理。
      • next是最重要的,支持无限个next的执行,必须要实现;而error和complete如果触发的话那么只会触发其中一个方法,因为如果异常了,那么就不会到complete中,如果到了complete中那么肯定没发生异常
      • Observable严格按照Observable契约,所以在complete以后是不会再执行next的,如下:
      • 查看代码
        import { Observable } from 'rxjs';
        
        const observable = new Observable(function subscribe(subscriber) {
          subscriber.next(1);
          subscriber.next(2);
          subscriber.next(3);
          subscriber.complete();
          subscriber.next(4); // 此处将不会被执行,因为在这前面以后调用了complete方法
        });
      • 我们可以在Subscribe函数中进行try-catch,有效的捕捉异常,如下:
      • 查看代码
        import { Observable } from 'rxjs';
        
        const observable = new Observable(function subscribe(subscriber) {
          try {
            subscriber.next(1);
            subscriber.next(2);
            subscriber.next(3);
            subscriber.complete();
          } catch (err) {
            subscriber.error(err); // 观察者中任意位置发生了异常都会执行此回调函数
          }
        });
    8. 取消
      • 当你订阅时,你会得到一个订阅,它代表正在进行的执行。只需调用unsubscribe()即可取消执行
      • 当我们使用创建 Observable 时,每个 Observable 都必须定义如何处理该执行的资源create();您可以通过unsubscribe从内部返回自定义函数来做到这一点function subscribe()

        例如,这是我们清除间隔执行集的方式setInterval

      • 查看代码
        const observable = new Observable(function subscribe(subscriber) {
          // Keep track of the interval resource
          const intervalId = setInterval(() => {
            subscriber.next('hi');
          }, 1000);
        
          // 提供一种取消和处理interval资源的方法
          return function unsubscribe() {
            clearInterval(intervalId);
          };
        });
        // 执行订阅
        const unsubscribe = observable.subscribe(x => console.log(x));
        
        // 在两秒以后取消订阅,将会调用上面重写的unsubscribe方法来停止hi的输出并且清楚interval资源
        setTimeout(() => {
          unsubscribe.unsubscribe();
        }, 2000);
    9.  与其他类型的比较
      • Function是一种延迟计算的计算,它在调用时同步返回单个值。
      • Iterator是一种延迟计算的计算,它在迭代时同步返回零到(可能)无限值。
      • Promise是一种可能(或可能不会)最终返回单个值的计算。
      • Observable是一种延迟计算的计算,从它被调用的那一刻起,它可以同步或异步返回零到(可能)无限值。
  3. Observer
    1. Observer 是 Observable 的消费者(Observable会生产数据,Observer对这些数据进行消费)。观察者只是一组回调,对应于 Observable 传递的每种类型的通知:nexterrorcomplete。下面是一个典型的 Observer 对象的例子:
      • 查看代码
        // 定义一个用于消费的observer
        const observer = {
          next: x => console.log('Observer got a next value: ' + x),
          error: err => console.error('Observer got an error: ' + err),
          complete: () => console.log('Observer got a complete notification'),
        };
        
        // 对observable进行订阅的时候将observer传入进行,用于消费数据
        observable.subscribe(observer);
        
        // 也可以使用简单的方式,不用传入observer对象,只需要传入一个回调函数就行
        observable.subscribe(x => console.log('Observer got a next value: ' + x));
  4. Operators
    1. Operator是一种基于Observable实例进行运算的函数,比如说过滤、合并、求平方...
    2. Operator分两种,一种是管道操作符,一种是创建操作符
      • 管道操作符主要是将第一个Observable作为参数输入并且返回一个新的Observable,但是其订阅逻辑是基于第一个Observable
      • 创建操作符主要是以独立函数的方式创建新的Observable
    3. 使用创建操作符创建一个新的Observable并且使用管道操作符进行求平方的运算,如下代码:
      • 查看代码
        import { of } from 'rxjs';
        import { map } from 'rxjs/operators';
        
        of(1, 2, 3)
          .pipe(map((x) => x * x))
          .subscribe((v) => console.log(`value: ${v}`));
        // Logs:
        // value: 1
        // value: 4
        // value: 9
    4. 上述代码中使用of创建一个新的Observable,并且使用map来对每一个值进行求平方,注意,在of后面使用了pipe方法,主要是因为operator就是普通的函数,可以被连续调用,所以如果有多个operator被调用,其代码就会变得可读性很差,比如说of(1,2,3).op4()(op3()(op2()(op1()(obs)))),所以使用pipe方式就是为了使其更具有可读性。
    5. 使用pipe()函数生成新的操作符
      • 有时候我们想把多个运算符写到一个方法里面,方便去重用或者方便定义,那可以使用pipe来实现(这个pipe和observable上的pipe名字一模一样,官方也没解释为什么这样命名),如下:
      • 查看代码
        import { pipe } from 'rxjs';
        import { filter, map } from 'rxjs/operators';
        
        discardOddDoubleEven() {
          return pipe(
            filter<number>((v) => !(v % 2)),
            map((v) => v + v)
          );
        }
        
        of(1, 2, 3)
        .pipe(this.discardOddDoubleEven())
        .subscribe((v) => console.log(`value: ${v}`));
        
        // 创建一个只对偶数值就行翻倍的函数
        
        // Logs: 4
    6. Rxjs提供的运算符很多,这里就不赘述了,请查看官网,按需使用:Rxjs操作符
  5. Subscription
    1. Subscription 是一个表示一次性资源的对象,对Observable的执行会返回一个Subscription对象,此对象有一个重要的无参方法unsubscribe,它是指取消订阅,我们可以在此方法中释放自己需要释放的资源,如下:
      • 查看代码
        
        const observable1 = interval(1000);
        const subscription = observable1.subscribe(x => console.log(x));
        // Later: 两秒以后将取消订阅,控制台中在输出了0和1以后将不再输出日志
        setTimeout(() => {
           subscription.unsubscribe();
        }, 2000);
        // Logs:0
        //      1
    2.  Subscription可以添加ChildSubscription,并且在Subscription调用unsubscribe方法以后将ChildSubscription也一并取消了,如下:
      • 查看代码
        
        import { interval } from 'rxjs';
        
        const observable2 = interval(400);
        const observable3 = interval(300);
         
        const subscription1 = observable2.subscribe(x => console.log('first: ' + x));
        const childSubscription = observable3.subscribe(x => console.log('second: ' + x));
         
        subscription1.add(childSubscription);
         
        setTimeout(() => {
          // 此处将取消subscription1和childSubscription的订阅
          subscription1.unsubscribe();
        }, 1000);
        
        // Logs:second: 0
        //      first: 0
        //      second: 1
        //      first: 1
        //      second: 2
  6. Subject
    1. 主题主要是用于多播场景,等用到的时候或者时间充足的时候再来记录吧,请查看官网:主题
  7. Schedulers
    1. 调度器这个可能涉及到原理,等深入去玩Angular的时候再来记录吧,请查看官网:调度器
posted @   北月、大魔王  阅读(346)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)
点击右上角即可分享
微信分享提示