RxJS:通过合并动态添加和删除Observable

时间:2019-06-09 10:24:09

标签: angular typescript rxjs rxjs6

我正在尝试构建可插入的ProgressService

该服务应跟踪当前正在加载的“物品”数量。并有一个isLoading()方法返回一个Observable<boolean>来指示是否正在加载任何东西。

我的第一个解决方案是使用new BehaviorSubject(0)非常幼稚,然后每个加载提供程序仅调用ProgressService.increase()ProgressService.decrease()。这很好。但是现在,如果可能的话,我想变得更加被动。


然后我遇到merge,当一开始就知道所有加载提供程序时,该方法就很好用了:

this.progress = merge(...progressProviders).pipe(
  scan((acc, curr) => acc + (curr ? 1 : -1), 0)
);

当任何加载提供程序发出progresstrue时,这只会增加/减少false的值。

但是我还需要某种注册/注销功能。基本上,这应该将新的Observable添加到merge链中(或将其删除)。


新的ProgressService应该如下所示:

class ProgressService {

  progress: Observable<number> = EMPTY; // value > 0 when something is loading

  addLoadingProvider(lp: Observable<boolean>) {
    // increment this.progress, when lp emits true
    // decrease this.progress, when lp emits false
    // nice to have: ignore if lp's first emitted value is false
  }

  removeLoadingProvider(lp: Observable<boolean>) {
    // stop listening to lp
    // clean up: decrease this.progress, if lp last emitted true
  }

  isLoading(): Observable<boolean> {
    return this.progress.pipe(
      map(val => val !== 0)
    );
  }

}

如果我们从removeLoadingProvider返回Subscription并使用addLoadingProvider取消注册,也许根本不需要Subscription.unsubscribe()方法(?)。

希望有人可以告诉我如何按需mergeunmerge其他可观察对象。

1 个答案:

答案 0 :(得分:0)

根据您的解释,我可以理解以下内容[如果我的理解不正确,请纠正我]-

您想要收集各种发出布尔值的可观察对象,并且如果其中任何一个具有至少一个"true",则要以这种方式进行跟踪,那么您的最终可观察对象应发出true,否则应返回最终可观察对象"false"

尽管您的BehaviorSubject方法是一种被动方法。我提议以下方法;让我知道您的情况是否可行-

方法1-

enum ListAction {
  Added,
  Removed,
  Empty,
  Undefined
}

export class ProgressService {

  constructor() { }

  progress: Observable<number> = EMPTY; // value > 0 when something is loading

  obsListChanged: BehaviorSubject<ListAction> = new BehaviorSubject<any>(ListAction.Undefined);
  obsList: Array<Observable<boolean>> = [];

  addLoadingProvider(lp: Observable<boolean>) {
    // increment this.progress, when lp emits true
    // decrease this.progress, when lp emits false
    // nice to have: ignore if lp's first emitted value is false
    this.obsList.push(lp);

    this.obsListChanged.next(ListAction.Added);
  }

  removeLoadingProvider(lp: Observable<boolean>) {
    // stop listening to lp
    // clean up: decrease this.progress, if lp last emitted true
    this.obsList = this.obsList.filter(i => i !== lp);
    this.obsListChanged.next(ListAction.Removed);
  }

  isLoading(): Observable<boolean> {
    // return this.progress.pipe(
    //   map(val => val !== 0)
    // );
    return this.obsListChanged.pipe(
      switchMap(() => {
        return combineLatest(this.obsList);
      }),
      map(v => {
        return v.some(loading => loading);
      })
    );
  }
}

如果您要按照ListAction进行特定的工作,那么我已经定义了一个ListAction可观察的对象,那么您可以按照逻辑在rxjs运算符中执行相同的操作。

方法2 [方法1的改进版]-

export class ProgressService {

  constructor() { }

  progress: Observable<number> = EMPTY; // value > 0 when something is loading

  obsList$: BehaviorSubject<Array<Observable<boolean>>> = new BehaviorSubject<Array<Observable<boolean>>>([]);

  addLoadingProvider(lp: Observable<boolean>) {
    // increment this.progress, when lp emits true
    // decrease this.progress, when lp emits false
    // nice to have: ignore if lp's first emitted value is false
    this.obsList$.next([...this.obsList$.getValue(), lp]);
  }

  removeLoadingProvider(lp: Observable<boolean>) {
    // stop listening to lp
    // clean up: decrease this.progress, if lp last emitted true
    const removed = this.obsList$.getValue().filter(i => i !== lp);
    this.obsList$.next(removed);
  }

  isLoading(): Observable<boolean> {
    // return this.progress.pipe(
    //   map(val => val !== 0)
    // );
    return this.obsList$.pipe(
      switchMap(obs => {
        return combineLatest(obs);
      }),
      map(v => {
        return v.some(loading => loading);
      })
    );
  }
}