缓冲并减少值,同时切换到另一个可观察的值

时间:2018-12-11 21:12:07

标签: rxjs observable reactive-programming

比方说,我们有一些object TypeClassTest { trait X trait Y[T] { def t: T } // Class with "static" method `t` class Z extends X // "static" method defined implicit object Z extends Y[String] { override def t = "t" } // Using 'static' method of the generic type class A[T <: X : Y](underlying: T) { val t: T = implicitly[Y[T]].t } } 可观察到并发出var values = new Array(); $('.selectList select').on('change', function(){ values = new Array(); $('.selectList select').find(":selected").each(function(){ values.push($(this).text()); }); console.log(values); });

input$

对于每个发射,我需要切换到另一个可观察到的(类似于Item)。但是,我需要确保所有这些可切换的观测值均完整且按顺序运行。足够容易地,我们有const input$: Observable<Item>; 可以做到这一点:

switchMap

但是,我想做的是:缓冲项目并减少它们(即,我有一个函数concatMap),同时这些可切换的可观察对象之一处于活动状态。更具体地说,假设input$.pipe(concatMap(item => processItem(item))) 。在这种情况下,我的减速器就是(a: Item, b: Item): Item

我们有很多type Item = {[key: string]: string}{...a, ...b}buffer*运算符可用,尽管我似乎找不到实现这种行为的 easy 组合

我可以很好地编写我的自定义运算符,但是我对是否可以将其表示为一些内置运算符的(简单的)组合感兴趣?

>

仅需说明一下:可观察的输出应该发出我们切换到的可观察的值,而不是缓冲/减少的值。另外,尽管源的完成/错误应反映在输出中,但任何正在进行的内部订阅都应首先完成。

我要查找的运算符应该基本上具有类似于

的签名
window*

为了完整性起见,这是我要寻找的操作员的大理石图。假定加法器为减速器,我们只是切换到输入,但要延迟四个刻度:

throttle*

在这里,bufferedConcatMap<T, R>( project: (value: T) => Observable<R>, reducer: (values: T[]) => T ): OperatorFunction<T, R>; 立即切换到我们的延迟(因为没有正在进行的内部订阅),并且在四个滴答声之后我们得到了结果。由于与此同时,Input: ---123--| Output: ------1--(5|) 1都已发出,因此它们被缓冲在一起并减小到2,这又在以后发出了四个滴答,因为我们仅在{之后{1}}已经回来。

1 个答案:

答案 0 :(得分:0)

更新12/13 :由于我的操作假设是内置运算符的简单组合无法在此处完成工作,因此我实现了自己的运算符。与我的原始要求相反,它的行为如下:

  • 如果源完成,则输出将首先等待活动的内部订阅完成,然后再完成外部可观察项。
  • 如果源错误,外部可观察对象将立即传播错误。这更符合concatMapexhaustMap之类的运算符。

我还没有为此编写测试套件,但是到目前为止看来还不错。

我将在此处you can also find a Stackblitz playground here中发布操作员的代码。

type Reducer<A, B> = (values: A[]) => B;
type Project<A, B> = (value: A) => ObservableInput<B>;

export function bufferReduceMap<A, B, R>(reducer: Reducer<A, B>, project: Project<B, R>): OperatorFunction<A, R> {
  return function (source: Observable<A>) {
    return source.lift(new BufferReduceMapOperator<A, B, R>(reducer, project));
  };
}

class BufferReduceMapOperator<A, B, R> implements Operator<A, R> {

  constructor(private reducer: Reducer<A, B>, private project: Project<B, R>) {}

  call(subscriber: Subscriber<R>, source: any): TeardownLogic {
    return source.subscribe(new BufferReduceMapSubscriber<A, B, R>(subscriber, this.reducer, this.project));
  }

}

class BufferReduceMapSubscriber<A, B, R> extends OuterSubscriber<A, B> {

  private buffer: A[] = [];
  private active = false;
  private hasCompleted = false;
  private hasErrored = false;

  constructor(
    destination: Subscriber<R>,
    private reducer: Reducer<A, B>,
    private project: Project<B, R>,
  ) {
    super(destination);
  }

  protected _next(value: A) {
    const buffer = this.buffer;
    buffer.push(value);

    this._tryNext();
  }

  protected _complete() {
    this.hasCompleted = true;
    if (!this.active && this.buffer.length === 0) {
      this.destination.complete();
    }

    this.unsubscribe();
  }

  public notifyComplete(innerSub: Subscription) {
    this.remove(innerSub);

    this.active = false;
    if (this.buffer.length !== 0) {
      this._tryNext();
    } else if (this.hasCompleted) {
      this.destination.complete();
    }
  }

  protected _tryNext() {
    if (this.active) {
      return;
    }

    let reduced: B;
    try {
      reduced = this.reducer(this.buffer);
    } catch (err) {
      this.destination.error(err);
      return;
    }

    let result: ObservableInput<R>;
    try {
      result = this.project(reduced);
    } catch (err) {
      this.destination.error(err);
      return;
    }

    this.active = true;

    const innerSubscriber = new InnerSubscriber(this, undefined, undefined);

    const destination = this.destination as Subscription;
    destination.add(innerSubscriber);

    this.buffer = [];
    subscribeTo<R>(result)(innerSubscriber);
  }

}