4

rxjs拥有大量的操作符可以说基本涵括日常使用的方方面面, 但总有一天会有没有符合要求的操作符的一天, 或者, 即使有, 但我们却没有找到, 毕竟已经存在的操作符已经很多了, 说不定会越来越多, 这时就需要我们来自定义操作符了。

本文针对 rxjs6 ,以前的版本可以查看这篇文章

为Operator添加操作符

在5.5版本, rxjs增加了pipe操作符, 在那之后, 自定义操作符的方式就很简单了, 基本就是实现一个函数, 官方文档中有如下描述:

基本上来说,pipeable 操作符可以是任何函数,但是它需要返回签名为<T, R>(source: Observable<T>) => Observable<R>的函数。
现在Observable中有一个内置的pipe方法 (Observable.prototype.pipe),它可以用类似于之前的链式调用的方式来组合操作符

所以我们只需要定义一个函数, 他的返回值签名是<T, R>(source: Observable<T>) => Observable<R>就可以了。

在使用一个例子开始以前,让我们看看编写操作符的注意事项

  • 操作符的返回值必须是Observable类型,没有第二种可能。
  • 一定要管理好subscription,这样订阅生产的资源才能及时被释放。
  • 处理好异常,因为你无法保证用户传进来的逻辑永远都可以正确运行。
  • 在合适的时间,处理好其它你需要释放的资源,例如流结时,或抛出错误时。

接下来让我们用一个简单的例子来实现以下

一个简单的例子

假如我们需要一个对数字进行平方的操作符:

  square = () => {
    return source => {
      return source.pipe(map((value: number) => value * value));
    };
  }

他的入参为空, 返回的函数的行参source就是即将被传入的observable,
然后我们就可以在,pipe()中调用了

  test() {
    const obs = of(1, 2, 3);
    // 1, 4, 9
    obs.pipe(square()).subscribe((value) => console.log(value)); 
  }

一个简单的操作符就完成了,但,是不是感觉还少了些什么,对,要是传入进来的不是一个数字呢,那时用户会收到一个NaN(Not a Number), 所以,我们还需要给他判断一下并报错

const square = () => source => source
  .pipe(map((value: number) => {
        // 判断传入的值是否为数字
      if (value && !isNaN(value)) {
        return value * value;
      }
      throw Error('收到的数据不是数值类型');
    }),
    catchError(err => of(err.toString())));

lift()

自定义操作符除了可以通过pipe()进行,还有很多地方提到了lift()这个方法, 并且很多地方都把 lift()pipe()作为对比,所以又去学习了一下lift()
image.png

创建一个新的Observable,以该Observable作为源,并将传递的运算符定义为新Observable的运算符。

它和pipe()的对比可以查看这篇文章
简单的说

lift()创建一个新的可观察对象,但pipe()没有。pipe()遵循函数式编程范式,lift()是面向对象的。

(后面这部分感觉理解的还不是很到位就不翻译了)

  • pipe's operator function maps an Observable to an Observable
  • lift's operator function maps an Observer to an Observer

This is just another way to represent the idea of either:

  • building an Observable chain down from the source to the sink
  • or building an Observer chain up from the sink to the source

如何使用lift()

rxjslift()如何使用呢, ,下面是一个简单的例子,改造后的square():

class Square implements Operator<number, number> {
  call(subscriber: Subscriber<number>, source: Observable<number>): void {
    source.subscribe(value => {
        if (value && !isNaN(value)) {
          subscriber.next(value * value);
        }
        throw Error('收到的数据不是数值类型');
      },
      error => console.log(error));
  }
}

使用

  test() {
    const obs = of(123);
    obs.lift(new Square()).subscribe((value) => console.log(value)); // 1, 4, 9
  }

上面只是一个简单的例子,若想通过lift()编写操作符,可以直接参考那些操作符的源码, lift()在源码中有大量的使用。

总结

写完以后能明显的感觉到这篇文章还有很大的改进空间,本来还想更细致探讨一下原理性的东西的,但是那一大堆花里胡哨的函数签名看的自己直发晕,而且现在汇报时间就要到了,还是算了,等自己水平再提升一些了再来看吧。

参考文章

Rxjs-自定义操作符
官方文档
What is the difference between Observable.lift and Observable.pipe in rxjs?


笙歌会停
1k 声望45 粉丝

代码成就万世基积沙镇海 梦想永在凌云意意气风发


引用和评论

0 条评论