确定Observable是否“可完成”

时间:2018-11-09 07:41:44

标签: rxjs

我经常遇到这样的情况,即我无法轻松确定可观察对象是否“可填充”。我所说的“完成”是什么意思?假设我们有:

service.ts

...

public getData(): Observable<number[]> {
    // Obviously in real application there would be something more meaningful
    return of([1, 2, 3]);
}

...

component.ts

...

public updateData(): void {
    this.serviceA.getData.subscribe(
        newData => this.data = newData
    );
}

...

在我们的service.ts中,我们有方法getData(),该方法通过Observable返回数据,并在完成后返回数据。

为什么我想知道它是否完成?因为它完成了,这意味着在我的组件被销毁之后,我不需要取消订阅。显然,在微不足道的情况下,这没什么大不了的,但是如果您在大型代码库中的团队中工作,则每次都需要弄清楚它。

2 个答案:

答案 0 :(得分:1)

在我看来,优良作法是将所有可观察到的事物视为永恒且永不“复杂”。没有直接的方法可以确定先验是特定的可观察的“可简化的”,因为这完全取决于可观察的逻辑,从用户的角度来看,这是私有的并且属于可观察的逻辑。与其进行假设,不如将每个订阅存储在控制器变量中,并且当控制器被销毁时,只需取消订阅所有订阅,这对已关闭的订阅也是安全的。
如果您要针对可观察到的完整性执行特定操作,请尝试在onComplete处理程序中实现它。

这种方法迫使您考虑以下所有边缘情况:

  • 不会发出任何值,并且可观察值将完成(空)
  • 不会发出任何值,并且无法观察到(永远不变的空值)
  • 将发出无限数量的值,并且可观察的值将不完整(永久)
  • 将发出一个或多个值并完成可观察的操作

答案 1 :(得分:1)

如前所述,没有办法仅凭Observable的类型推断它是否会在将来的任何时间点完成。 Observable Contract不包含此类语义。但是,我可以想象以下解决方案:

1:让其他人处理退订

我发现,当我不是Observable的最终用户时,不参与订阅或取消订阅是一种很好的做法。最终消费者是指最终使用通知的组件,例如显示数据。

我尽可能地使用Angular async管道来代替自己的订阅和退订。在大多数情况下,可以使用Observable pipes处理数据的转换,这样,我所有的中间组件都只会添加管道,而只有view组件最终会订阅结果Observable。如果您的视图框架尚不支持此功能,我将尝试找到一个插件,甚至自己实现。

例如,我没有像您的示例那样将observable的结果分配给class属性,而是仅使用Angular模板来订阅Observable。该组件已修改:

public data$: Observable<number[]>;

public updateData(): void {
    // here, modification would take place with pipes
    this.data$ = this.serviceA.getData();
}

最后在模板中,我将使用async

<div *ngIf="(data$ | async) as data">
    {{ data }}
<div>

2:引入一种新类型

尽管原始的可观察合同不包含处理您的问题的语义,但是我们可以引入一个自己的类型,该类型将Observable括起来以表明该可观察对象最终将完成。为了保证这种行为,我们可以添加一个超时,如果在给定时间内没有通知,则该超时将使可观察的事件完成。看起来可能像这样:

class ShortLivedObservable<T> extends Observable<T> {
    private constructor() {
        super();
    }
}

function asShortLivedObservable<T>(source: Observable<T>, timeout: number = 1000): ShortLivedObservable<T> {
   return source.pipe(
       switchMap(value =>
           merge(
               of(value).pipe(
                   map(value => ({value: value}))
               ),
               timer(timeout).pipe(
                   map(() => ({complete: true}))
               )
           )
       ),
       takeUntil(({value, complete}) => complete),
       map(({value}) => value)
   )
}

但是,此解决方案将带来缺点,即引入了不遵循ReactiveX惯例的新语义。该类型将必须传递给最终使用者,并且进一步的管道传输可能再次使可观察到的泄漏(请参阅此处描述的问题:RxJS: Avoiding takeUntil Leaks)。