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?
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.
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.
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.
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.
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.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With