RxJS 系列 – Custom Operator

前言

虽然 RxJS 提供了非常多的 Operators. 但依然会有不够用的时候. 这时就可以自定义 Operator 了.

 

Operator Is Just a Function Observable => Observable

Operator 只是一个函数.

timer(1000).pipe(obs => obs).subscribe();

它接收一个 Observable 返回一个 Observable.

Operator 都是放在管道 (pipe) 内的, 所以从源头 Observable 一直传递下去.

每一个 Operator 接收 upstream(上游) 的 Observable 并且返回一个 Observable (通常是新的一个) 给 downstream(下游).

每一个 Operator 就是对发布值的加工处理.

 

Standard Operator

source 源头

首先我们有个源头

const source = timer(1000);

每一秒发布一次

Upstream / Downstream Observable

接着搞一个 pipe 和 operator.

const source = timer(1000);
source
  .pipe(upstreamObs => {
    console.log(upstreamObs === source); // true

    const downstreamObs = new Observable();
    return downstreamObs;
  })
  .subscribe(v => console.log(v));

可以看到 operator 函数接收的就是 upstream 的 Observable, 然后返回一个新的 Observable 到 downstream.

Connect Upstream and Downstream

downstream to upstream

当 downstream 被 subscribe, 我们 subscribe upstream

当 downstream 被 unsubscribe, 我们 unsubscribe upstream

const source = timer(1000);
source
  .pipe(upstreamObs => {
    const downstreamObs = new Observable(subscriber => {

      // 当 downstream Observable 被订阅以后, 我们才订阅 upstream Observable
      const upstreamSub = upstreamObs.subscribe();

      return () => {
        // 当 downstream 被退订以后, 我们也要退订 upstream Observable
        upstreamSub.unsubscribe();
      };
      
    });

    return downstreamObs;
  })
  .subscribe(v => console.log(v));

upstream to downstream

当接收到 upstream 发布时, 我们也发布到 downstream

const downstreamObs = new Observable(subscriber => {
  const upstreamSub = upstreamObs.subscribe({
    next: upstreamValue => {
      const downStreamValue = upstreamValue + 'downstream value'; // decorate value for downstream
      subscriber.next(downStreamValue); // 发布到 downstream
    },
    error: error => subscriber.error(error), // 转发去 downstream
    complete: () => subscriber.complete(),   // 转发去 downstream
  });

  return () => {
    upstreamSub.unsubscribe();
  };
});

小结

从上面几招可以看出来, 主要就是 upstream 和 downstream 中间的关系.

如何订阅, 退订, 发布 value 等等. 上面只是给一个简单直观的例子. 真实场景中, upstream 和 downstream 发布时机, 往往是不一致的 (比如 upstream 发布 1 次后, downstream 可能发布多次)

Operator with Config

只要把 Operator 包装成工厂函数就可以支持 Config 了

function myOperator(config: { dependObs: Observable<unknown> }): OperatorFunction<number, string> {
  const { dependObs }= config;  
  return upstreamObs => {
    const downstreamObs = new Observable<string>(subscriber => {

      // 订阅 depend
      const dependObsSub = dependObs.subscribe(() => console.log('do something'));

      const upstreamSub = upstreamObs.subscribe({
        next: upstreamValue => {
          const downStreamValue = upstreamValue + 'downstream value'; // decorate value for downstream
          subscriber.next(downStreamValue); // 发布到 downstream
        },
        error: error => subscriber.error(error), // 转发去 downstream
        complete: () => {
          // 释放 depend
          dependObsSub.unsubscribe();
          subscriber.complete();
        },   
      });

      return () => {
        upstreamSub.unsubscribe();
        // 释放 depend
        dependObsSub.unsubscribe();
      };
    });

    return downstreamObs;
  };
}
const source = timer(1000);
const dependObs = new Subject();
source.pipe(myOperator({ dependObs })).subscribe(v => console.log(v));

一个 Factory 函数, 通过配置生产 Operator。

OperatorFunction 定义了 Operator 接口, 它是加入了泛型的 Observable => Observable.

如果 upstream 和 downstream 的类型是一样的话可以用它的简化版本 MonoTypeOperatorFunction<T>

Unsubscribe dependency stream

比如说像 takeUntil operator

const obs = timer(0, 1000); // 0..1..2..3..4..5
obs.pipe(takeUntil(timer(3000))).subscribe(v => console.log(v)); // 0..1..2

它内部会 subscribe timer$,timer$ 就是它的 dependency observable。

如果在还没有到 3 秒的时候,这个 observable 就被 unsubscribe 了,那 takeUntil operator 内部就要提前去 unsubscribe timer$。

同理,如果这个 observable 在还没有到 3 秒的时候,就被 error 或 complete 了,那 takeUntil operator 内部也需要提前去 unsubscribe timer$。

总之,我们在设计 operator 时,一定要顾虑到 upstream 的 error / complete 和 downstream 的 unsubscribe 对 dependency observable 的影响。

 

posted @ 2022-11-02 22:26  兴杰  阅读(63)  评论(0编辑  收藏  举报