Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between Observable.lift and Observable.pipe in rxjs?

The docs define Observable.lift(operator: Operator) as:

Creates a new Observable, with this Observable as the source, and the passed operator defined as the new observable's operator.

and Observable.pipe(operations: ...*) as:

Used to stitch together functional operators into a chain. Returns the Observable result of all of the operators having been called in the order they were passed in.

So clearly .pipe can accept multiple operators, which .lift cannot. But pipe can also accept a single operator, so this cannot be the only difference. From the docs alone it isn't clear to me what they are both for and why they exist. Can someone please explain the purpose of each of these functions, and when each of them should be used?


Observations so far

The following code (typescript):

let myObservable = Observable.of(1, 2, 3);
let timesByTwoPiped = myObservable.pipe(map(n => n * 2));
let timesByTwoLift = myObservable.lift(new TimesByTwoOperator());

timesByTwoPiped.subscribe(a => console.log('pipe:' + a));
timesByTwoLift.subscribe(a => console.log('lift:' + a));

and TimesByTwoOperator:

class TimesByTwoOperator implements Operator<number, number> {
  call(subscriber: Subscriber<number>, source: Observable<number>): void | Function | AnonymousSubscription {
    source.subscribe(n => {
      subscriber.next(n * 2);
    });
  }
}

Seems to achieve the same result using both .lift and .pipe. This experiment shows I'm correct in thinking that both lift and pipe can be used to achieve the same thing, albeit with the pipe version being more succinct in this case.

As the Operator type that is passed in to .lift is given full access to the source observable and subscriptions, clearly powerful things could be achieved with it; for example keeping state. But I'm aware that the same sort of power can also be achieved with .pipe, for example with the buffer operator.

It's still not clear to me why they both exist and what each is designed for.

like image 237
George Powell Avatar asked Feb 22 '18 11:02

George Powell


People also ask

What does Observable pipe do?

The pipe method of the Angular Observable is used to chain multiple operators together. We can use the pipe as a standalone method, which helps us to reuse it at multiple places or as an instance method.

What is pipe () in RxJS?

RxJS' pipe() is both a standalone function and a method on the Observable interface that can be used to combine multiple RxJS operators to compose asynchronous operations. The pipe() function takes one or more operators and returns an RxJS Observable.

What is the difference between Observable and a subject in RxJS?

An RxJS Subject is a special type of Observable that allows values to be multicasted to many Observers. While plain Observables are unicast (each subscribed Observer owns an independent execution of the Observable), Subjects are multicast. A Subject is like an Observable, but can multicast to many Observers.

What is the difference between Observable and subscription?

Observables are not executed until a consumer subscribes. The subscribe() executes the defined behavior once, and it can be called again. Each subscription has its own computation. Resubscription causes recomputation of values.


2 Answers

I've found a nice in-depth discussion on this subject and the potential idea of removing Observable.lift in favor of Observable.pipe here: https://github.com/ReactiveX/rxjs/issues/2911

TL;DR

Now let's compare the "pure" lift and pipe signatures:

// I'm intentionally ignoring pipe's multiple operator function args,
// since we could redefine lift to also take multiple operator functions
type Operator = <T, R>(sink: Observer<R>) => Observer<T>
type lift = <T, R>(src: Observable<T>, op: Operator) => Observable<R>;
type pipe = <T, R>(op: Operator) => (src: Observable<T>) => Observable<R>
  • pipe's operator function maps an Observable to an Observable
  • lift's operator function maps an Observer to an Observer

This is just another way to represent the idea of either:

  • building an Observable chain down from the source to the sink
  • or building an Observer chain up from the sink to the source
like image 72
JHeut Avatar answered Oct 11 '22 09:10

JHeut


Can someone please explain the purpose of each of these functions, and when each of them should be used?

lift() creates a new observable object but pipe() does not. pipe() follows the functional programming paradigm and lift() is object-oriented.

They both accept functions as input arguments, but the advantage of pipe() is that there is no extra observable created.

When you use lift() a single operator is attached to a new observable, and when this new observable is subscribed the attached operator intercepts the stream before it is subscribed too.

This is different from how pipe() works because an operator returns the same observable will yield no changes to the original observable.

pipe() was introduced after lift() and I think this is the preferred way to chain operators.

like image 29
Reactgular Avatar answered Oct 11 '22 10:10

Reactgular