RxJS沉浸式入门教程

什么是RxJS【Reactive Extensions for JavaScript】

首先RxJS是一个库,是针对异步数据流编程工具,当然Angular引入RxJS就是让异步更加简单,更加可控,在开始RxJS之前,我们先来了解一下Reactive Programming,其本质就是使用流(stream)的一种编程方式。

什么是流呢?

首先看示例:

例子:有变量 a和b,  b 永远等于 a + 1
// 基础写法
let a = 1;
b = a + 1;
console.log(b)
a = 2;
b = a + 1; // 每次a变化,都要执行一次b = a+ 1
console.log(b)

 
// 使用流来解决:
// 用 stream 描述 a 与 b 的关系。
const a$ = new Subject(); 
const b$ = a$.pipe(map( x=> x + 1)); 
b$.subscribe(console.log); 
a$.next(1); 
a$.next(2);

虽然在这里我们还不认识Subject是个什么,但是通过这个例子我们可以了解到,所谓流/stream,就是数据基于事件(event)变化的整体。stream = data + event,只要a变化,那么b就会自动跟着变化。

流的最大的好处是,能够监听数据的变化,执行相应的操作,从而最大可能性的减小性能的开销。对于rxjs而言任何东西都可以是streams

RxJS中的核心概念(Observable 、Observer 、Subscription、Subject)

在Angular项目中我们在调用接口的时候,常用的调用方式是:

this._exampleService.reportCurrentOpenTab$
       .subscribe((res: number) => {
         this.currentIndex = res;
       })
 //this._exampleService.reportCurrentOpenTab$ 这是返回observable,他可以是api的调用,可以是事件的调用等等

我们可以把上述的调用方式抽象一下为Observable.subscribe(observer),在这里我们认识到了两个新的事物分别是Observable和Observer,以及这个方法调用的返回对象,返回的是一个Subscription对象的实例化,接下来我们逐一介绍这些核心概念。

Observable

Observable是RxJS中最核心的一个概念,它的本质就是“Observable is a function to generate values”,首先它是一个函数,也就是说它是数据源头是数据生产者,一般我们会在变量末尾加$表示Observable类型的对象。

// 此函数定义了setInterval 每两秒产生一个 value的功能
const observable$ = (observer) => {
    let counter = 0;
    const id = setInterval(() => observer.next(counter++), 2000);
}
// 因为Observable是个对象,所以需要调用才可以执行
observable$({ next: (val) => console.log(val) });

函数中会定义 value 的生成方式,函数调用时,observer.next 来执行在observer 中定义的行为,比如上述示例中的counter++。

  1. 必须调用(订阅)才会被执行
  2. observable 被调用后,必须能被关闭,否则会一只运行下去
  3. 对于同一个 observable,在不同的地方 subscribe,是无关的。function 执行多次,互相没有关联是一致的

Observer

它是观察者数据使用者数据消费者。它是一个有三个回调函数的对象,每个回调函数对应三种Observable发送的通知类型(next, error, complete),observer表示的是对序列结果的处理方式。在实际开发中,如果我们提供了一个回调函数作为参数,subscribe会将我们提供的函数参数作为next的回调处理函数。next决定传递一个什么样的数据给观察者

let observer = {
next: data => console.log('data'); // next表示数据正常流动,
error: err=> console.log('err'); // error表示流中出错
complete: () => console.log('complete') // complete表示流结束
}
// error和complete只会触发一个,但是可以有多个next

Observables 与 Observer 之间的订阅发布关系(观察者模式) 如下:

订阅 :Observer 通过 Observable 提供的 subscribe() 方法订阅 Observable。
发布 :Observable 通过回调 Next 方法向 Observer 发布事件。

Subscription

observable.subscribe():是一个Subscription对象。在 observable.subscribe 内部,他会创建一个观察者对象并使用第一个回调函数作为next的处理方法。Subscription就是表示Observable 的执行,可以被清理。这个对象最常用的方法就是unsubscribe方法。同时,它还有add方法也可以使我们取消多个订阅,所以我们在Angular项目中会看到如下代码,去取消订阅:

    // 定义Subscription类型的数组同于存放页面中的observable订阅过程中产生的Subscription
    public subscriptions: Subscription[] = [];
    
    public ngOnInit(): void {
     this.subscriptions.push( // 将Subscription push到定义的数组中去
       this._layoutStatusService.reportCurOpenTab$
         .subscribe((res: number) => {
       this.currentIndex = res;
     })
 );
}

   // 在Angular的销毁生命周期中进行取消订阅,已达到优化性能的作用
   public ngOnDestroy(): void {
     this.subscriptions.forEach((subscription) => subscription.unsubscribe());
   }

我们可以用生活中 “订阅报刊” 的例子,再来理解一下订阅关系:
报刊生产商【observable】
市民【observer】

市民打电话给报刊生产商进行订阅【subscribe】,市民就会在报刊商送报纸(next)成功的时候收到报纸,这个时候市民是不用关心报纸是怎么生产和运送的,当然当报刊生产商可能会意外的导致送报纸失败(error),而等成功之后,报刊生产商还是会把这次送报标记为已送达(complete)

Subject

Subject是特殊的observable:我们可以像订阅任何observable一样去订阅subject。
Subject是观察者: 它有next(v),error(e),和complete()方法,如果我们需要给subject提供新值,只要调用next(v),它会将值多播给已注册监听该subject的观察者。
所以: Subject既是Observable,也是观察者(可以多个)

Subject与Observable的区别:

  • Subject是多播的【他可以将值多播给多个观察者】
  • 普通的Observble是单播的【每个已经订阅的观察者(observer)都拥有observable的独立执行,上述Observble的介绍也有提及】

Subject中常用的子类(ReplaySubject、AsyncSubject、BehaviorSubject)

这里针对Subject的这三个衍生子类,只做简单的介绍,在后续开发过程中如果需要使用该方法,请自行查阅官网,进行进一步的深入了解。

  • BehaviorSubject: 会保存最先发送数据,当被订阅的时,会立即使用这个最新【最先的】数据,然后会继续接收新的next的值。BehaviorSubject必须设置默认值,因为他有一个最新值(当前值)的概念。
  • ReplaySubject:会保存所有值,然后回放给最新的订阅者,当新的订阅发生的时候,会把上一次订阅的所有值都再次打印一遍~
  • AsyncSubject:只有当Observable执行完成时complete(),它才会执行的最后一个值发送给观察者,就是说会保留流里最后一条数据,而且只会在数据流complete时候才会发送。

Subject的在Angular中的常见的作用:

可以在Angular通过service来实现不同组件,或者不同模块之间的传值

// 定义公共的用于数据存储的service,文件名是(eg:xampleStore.service.ts)
    @Injectable()
     export class ExampleStoreService {
      private currentTabNumber$ = new Subject<number>();
    }
        
// 此数据更改的逻辑,可以在任何需要更改的地方进行next相对应的值,文件名是 (eg:a.component.ts)
   this.ExampleStoreService.currentTabNumber$.next(1);
   
// 订阅接收到数据更改,并做下一步逻辑处理,文件名是(eg:b.component.ts)
  this.ExampleStoreService.currentTabNumber$
        .subscribe((res: number) => {
          this.currentIndex = res;
        })

RxJS的操作符(Operator)简介

operators是个纯函数,它的输入为observable,返回也observable。operators的本质是,描述从一个数据流到另一个数据流之间的关系,也就是observer到observable中间发生的转换,很类似于Lodash。
在RxJS中操作符有接近100个,不过在开发过程稿常用的也就十多个。

常见的运算符包含 map,filter,concat,flatmap,switchmap,forkjoin
在这里我们只调挑出forkjoin来讲解一下,其他的操作符可以自己去查阅使用
forkjoin主要是用于多个接口都返回的时候,才会返回结果,有点类似于promise中的promise.all,具体的用法如下:

forkJoin([
      this._aService.getData(),
      this._bService.getData()
    ]).subscribe(resArr => {
      // 此时的返回结果会被按顺序放在一个数组中
      aData = resArr[0];
      bData = resArr[1];
  }

Observable与Promise的比较:

observable.subscribe(function next(value) {});
promise.then(function resolve(value) {});

从方法的调用上感觉这两种处理异步的形式是相似的,实际上observable比promise强大很多。具体对比如下:

Observable:

  • RxJS 里面用的是 next() 和 subscribe()
  • next 会被调用多次,可以发射多个值
  • 但是observable在没有 subscribe 的情况下,是不是被执行的。
  • RxJS unsubscribe可以取消订阅
  • RxJS还提供了大量的操作符,可以方便出来各种流的情况

Promise:

  • Promise里面用的是 then() 和 resolve()
  • resolve 只会被调用一次,不能多次触发异步调用,
  • then并不会触发promise的执行。也就是说,一个 promise不管有没有then, 都会被执行。

Observable与Promise的转换

RxJS是有提供方法,用来进行observable与promise之间转换的,所以在其他使用promise的项目里完全可以使用RxJS去迭代开发。

const promise = observableFun$.toPromise() // 将observable转换成promise
const observable = observable.fromPromise(promiseFun) // 将promise类型函数转换成observable

通过一个搜索示例,再来回顾一下今天介绍的RxJS

普通方式:

<input id="inputControl"></input>
<script>
    var inputControl = document.querySelector('#inputControl'),
        timer = null,
        currentSearchText = '';

    text.addEventListener('keyup', (e) =>{
        clearTimeout(timer)
        timer = setTimeout(() => {
      
            currentSearchText = ''; 

            var searchText = e.target.value;
            $.ajax({
                url: `/search/${searchText}`,
                success: data => {
                    if (data.search === currentSearchText) {
                        // 渲染展示
                        render(data);
                    } else {
                        // ..
                    }
                }           
            });
        },300)
    })
</script>

使用流的方式:

var inputControl = document.querySelector('#inputControl');
var inputStream = fromEvent(inputControl, 'keyup')
                    .pipe(
                      .debounceTime(300) // 防抖动
                      .pluck('target', 'value') // 使用pluck操作符,获取输入的值
                      .switchMap(url => Http.get(url)) // 将当前输入流替换为http请求
                    .subscribe(data => render(data)); // 接收数据

总结

上述介绍的内容几乎都是Rxjs中很基础的东西,RxJS的入门确实有些难度,但是如果掌握了之后,你会发现RxJS这种响应式编程是真的香,本篇文章中并没有着重介绍RxJS中的操作符,如果想了解更多关于操作符可以参考RxJS的操作符介绍

Rxjs官网:https://rxjs.dev/api

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值