【问题标题】:RxJs: Abort a deferred and shared observable when all unsubscribeRxJs:当全部取消订阅时,中止一个延迟和共享的 observable
【发布时间】:2021-06-22 08:57:37
【问题描述】:

我想创建一个运行长轮询操作的rxjs Observable。 每次迭代都会发出中间结果。 当isComplete 返回真时,Observable 完成。

这个函数的行为应该如下

  1. 只有当有至少一个订阅者时才应该开始
  2. 应该允许多个订阅者共享结果
  3. 如果没有没有订阅者离开
  4. ,它应该中止轮询并取消呼叫

以下代码运行正常,满足条件(1)和(2):

function longPollingAction(fetch: () => Promise<Response>, cancel: () => {}): Observable<Response> {
   return defer(() => { // defer to start running when there's a single subscriber
     return from(fetch()).pipe(
         expand(() => timer(1000).pipe(switchMap(fetch))),
         takeWhile<Response>(isComplete, false),
    );
   }).pipe(share()); // share to allow multiple subscribers
}

function isComplete(r: Response): boolean {
   // returns true if r is complete. 
}

如何修改此代码以满足 (3) 的要求?在当前实现中,轮询停止,但我如何调用cancel

【问题讨论】:

  • 我认为 share 已经有这种行为了?如果没有,请尝试.pipe(publish(), refCount())
  • @MrkSef 你是对的。轮询确实会随着当前的实现而停止。我解决了我的问题:当没有订阅者时,我想调用 cancel 函数。
  • 我在下面添加了一个答案。您应该可以只使用finalize

标签: javascript typescript rxjs observable reactivex


【解决方案1】:

使用终结

您可以使用finalize 调用取消。这可能是这样的:

function longPollingAction(
  fetch: () => Promise<Response>,
  cancel: () => void
): Observable<Response> {
  // defer to turn eager promise into lazy observable
  return defer(fetch).pipe( 
    expand(() => timer(1000).pipe(switchMap(fetch))),
    takeWhile<Response>(isComplete, false),
    finalize(cancel),
    share() // share to allow multiple subscribers
  );
}

function isComplete(r: Response): boolean {
   // returns true if r is complete. 
}

回调complete

tap 操作员可以访问nexterrorcomplete 排放。对于callback: () =&gt; void,这就足够了。

function longPollingAction(
  fetch: () => Promise<Response>,
  cancel: () => void
): Observable<Response> {
  // defer to turn eager promise into lazy observable
  return defer(fetch).pipe( 
    expand(() => timer(1000).pipe(switchMap(fetch))),
    takeWhile<Response>(isComplete, false),
    tap({
      complete: cancel
    }),
    share() // share to allow multiple subscribers
  );
}

function isComplete(r: Response): boolean {
   // returns true if r is complete. 
}

回调unsubscribe

我不认为这样的运算符存在,但我们可以很容易地制作一个。如果取消订阅,此运算符只会触发回调。它将忽略errorcomplete

function onUnsubscribe<T>(
  fn: () => void
): MonoTypeOperatorFunction<T> {
  return s => new Observable(observer => {
    const bindOn = name => observer[name].bind(observer);
    const sub = s.subscribe({
      next: bindOn("next"),
      error: bindOn("error"),
      complete: bindOn("complete")
    });
   
    return {
      unsubscribe: () => {
        fn();
        sub.unsubscribe()
      }
    };
  });
}

那么你可以这样使用它:

function longPollingAction(
  fetch: () => Promise<Response>,
  cancel: () => void
): Observable<Response> {
  // defer to turn eager promise into lazy observable
  return defer(fetch).pipe( 
    expand(() => timer(1000).pipe(switchMap(fetch))),
    takeWhile<Response>(isComplete, false),
    onUnsubscribe(cancel),
    share() // share to allow multiple subscribers
  );
}

function isComplete(r: Response): boolean {
   // returns true if r is complete. 
}

由于share 正在管理您的订阅并且共享只会取消订阅一次refCount &lt; 1,因此在这种情况下调用取消的唯一方法是没有订阅者。

【讨论】:

  • finalize 在流完成时也会被调用。不仅在执行停止时。
  • 它也会在出错时被调用。所有这些都会导致您的长轮询停止。
  • 我已经用更多选项更新了这个答案。
【解决方案2】:

给猫剥皮的方法不止一种,但我会这样做:

const onUnsubscribe = (callback: () => void) => <T>(source$: Observable<T>) =>
  new Observable<T>(observer => {
    let isSourceDone = false;

    const subscription = source$.subscribe(
      val => {
        observer.next(val);
      },
      e => {
        isSourceDone = true;
        observer.error(e);
      },
      () => {
        isSourceDone = true;
        observer.complete();
      }
    );

    return () => {
      if (isSourceDone) return;
      callback();
      subscription.unsubscribe();
    };
  });

function longPollingAction(
  fetch: () => Promise<Response>,
  cancel: () => {}
): Observable<Response> {
  const lazyFetch$ = defer(() => fetch());
  return lazyFetch$.pipe(
    expand(() => timer(1000).pipe(mergeMapTo(lazyFetch$))),
    takeWhile<Response>(isComplete, false),
    onUnsubscribe(cancel),
    share()
  );
}

function isComplete(r: Response): boolean {
  // returns true if r is complete.
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-08-11
    • 2020-08-29
    • 2017-12-17
    • 2016-07-07
    • 2020-05-25
    • 2018-04-03
    • 2021-08-08
    • 1970-01-01
    相关资源
    最近更新 更多