Reactgular
Reactgular

Reputation: 54741

Does the Observable operator takeUntil() depend upon it's order in pipe()

Is the operator takeUntil affected by other operators, and do you have to use it twice inside a switchMap?

For example;

Assume that I have an observable that emits a value when I no longer want to subscribe, and we'll refer to this as this._destroyed.

Does it matter if there is a delay operator before a takeUntil?

 of("something").pipe(
      delay(1000),
      takeUntil(this._destroyed)
 );

Is the above any different from reversing the order?

 of("something").pipe(
      takeUntil(this._destroyed),
      delay(1000)
 );

What if I use switchMap do I have to call takeUntil twice?

 of("something").pipe(
      takeUntil(this._destroyed),
      delay(1000),
      switchMap(() => {
          return of("other").pipe(
             takeUntil(this._destroyed),
             delay(1000)
          );
      }
 );

Is the above functionally the same as calling takeUntil once?

 of("something").pipe(
      delay(1000),
      switchMap(() => {
          return of("other").pipe(delay(1000));
      }),
      takeUntil(this._destroyed)
 );

I guess I'm confused as to what happens when takeUntil is triggered and stops the current subscription. How is it impacted by when it is called in the pipe order (if there is any impact at all).

Upvotes: 18

Views: 10333

Answers (1)

rfestag
rfestag

Reputation: 2008

My understanding is that generally takeUntil should be the last operator in a chain because it can result in leaks. This article describes the issue: https://ncjamieson.com/avoiding-takeuntil-leaks/

The article uses the following example:

import { combineLatest, Observable } from "rxjs";
import { takeUntil } from "rxjs/operators";

declare const a: Observable<number>;
declare const b: Observable<number>;
declare const notifier: Observable<any>;

const c = a.pipe(
  takeUntil(notifier),
  o => combineLatest(o, b)
).subscribe(value => console.log(value));

From the article:

When the notifier emits, the observable returned by the takeUntil operator completes, automatically unsubscribing any subscribers.

However, the subscriber to c is not subscribed to the observable returned by takeUntil — it’s subscribed to the observable returned by combineLatest — so it’s not automatically unsubscribed upon the takeUntil observable’s completion.

The subscriber to c will remain subscribed until all of the observables passed to combinedLast complete. So, unless b completed before the notifier emitted, the subscription to b would leak.

While this post specifically refers to switchMap, I suspect the logic is the same. It may be less of an issue with switchMap, as there is only one inner observable.

Upvotes: 16

Related Questions