Hivaga
Hivaga

Reputation: 4536

RXJS pipe chaining vs single pipe

I was wondering is there any difference between using a single myObservable.pipe(...) vs chaining myObservable.pipe(...).pipe(...).

Question is are those 2 examples identical? Is there a use case for pipe chaining?

Example with single pipe:

from([1, 2, 3])
  .pipe(
    delay(1000),
    map((value: number) => value * 2),
    map((value: number) => value * 3)
  )
  .subscribe((value) => {
    console.log('result:', value);
  });

Example with chaining pipes:

   from([1, 2, 3])
      .pipe(delay(1000))
      .pipe(map((value: number) => value * 2))
      .pipe(map((value: number) => value * 3))
      .subscribe((value) => {
        console.log('result:', value);
      });

Upvotes: 5

Views: 1787

Answers (2)

Mujadid Mughal
Mujadid Mughal

Reputation: 2663

RxJS is known for its operators. Essentially there is a kind of operators that take an observable as input and return another observabl, these are pipeable operators for example filter, map etc.

A Pipeable Operator is a function that takes an Observable as its input and returns another Observable. It is a pure operation: the previous Observable stays unmodified.

Now, the pipe operator just gives us syntax flexibility, to combine operators (logically thinking they are inside a pipe, where one would be run after the other).

Theoretically it doesn't make a difference that you chain pipe operators, but the sole purpose of pipe operator is to combine multiple operators, so its a good practice to combine group of operators inside a single pipe.

Upvotes: 1

Josep
Josep

Reputation: 13071

No, there isn't. However, I would like to help you understand why:

This code:

from([1,2,3]).pipe(
  delay(1000)
)

is 100% equivalent to:

delay(1000)(
  from([1, 2, 3])
)

And this code:

from([1, 2, 3])
  .pipe(
    delay(1000),
    map((value: number) => value * 2),
  )

is the same as:

map((value: number) => value * 2)(
  delay(1000)(
    from([1, 2, 3])
  )
)

Etc, etc.

The thing is that piepable operators (the ones that you can use inside pipe) are "Observable enhancers" (a type of higher order functions). What those functions return is a function that will enhance the Observable that has received as an argument and it will return a new Observable with some "enahnced" behavior.

Therefore, pipe is just sugar for composing those Observable enhancers in a more declarative manner.

For instance, a basic implementation of the map piepable operator would look like this:

const map = <I, O>(mapper: (input: I) => O) =>
  (source$: Observable<I>): Observable<O> =>
    new Observable<O>(observer => source$.subscribe({
      next: (value) => {
        observer.next(mapper(value));
      },
      error: (e) => {
        observer.error(e);
      },
      complete: () => {
        observer.complete();
      }
   }))

Upvotes: 3

Related Questions