Skip to content
This repository was archived by the owner on Nov 18, 2024. It is now read-only.

Commit 86bad08

Browse files
committed
Added "Choosing between lift and compose"
1 parent 58f4dbe commit 86bad08

File tree

1 file changed

+14
-4
lines changed

1 file changed

+14
-4
lines changed

Part 3 - Taming the sequence/7. Custom operators.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,9 +183,9 @@ Internally, every Rx operator does 3 things
183183
2. It transforms the observed sequence according to the operator's purpose.
184184
3. It pushes the modified sequence to its own subscribers, by calling `onNext`, `onError` and `onCompleted`.
185185

186-
The `compose` operator works with a method that makes an observable out of another. By doing this, it spares you the trouble of doing the 3 steps above manually. That presumes that you can do the transformation by using existing operators. If the operators don't already exist, or if you think you can get better performance manually, you need to receive items manually, process them and re-push them. An `Observable.Transformer` that does this would include a `subscribe` to the source `Observable` and the creation of a new `Observable` to be returned, possibly a `Subject`.
186+
The `compose` operator works with a method that makes an observable out of another. In doing so, it spares you the trouble of doing the 3 steps above manually: the intermediate subscribing and pushing is implicit within an Rx chain. That presumes that you can do the transformation by using existing operators. If the operators don't already exist, or if you think you can get better performance manually, you need to receive items manually, process them and re-push them. An `Observable.Transformer` that does this would include an explicit `subscribe` to the source `Observable` and the creation of a new `Observable` to be returned, possibly a `Subject`.
187187

188-
There's a simpler way with `lift`. The `lift` operator is similar to `compose`. Instead of transforming `Observable`, it transforms `Subscriber`.
188+
There's a simpler way with `lift`. The `lift` operator is similar to `compose`. Instead of transforming `Observable`, it transforms `Subscriber`. The boilerplate of subscribing with be handled by `lift`.
189189

190190
```java
191191
public final <R> Observable<R> lift(Observable.Operator<? extends R,? super T> lift)
@@ -299,7 +299,7 @@ Completed
299299
Unsubscribed
300300
```
301301

302-
Despite what our observable tried to emit, the end result obeyed the Rx contract. That happened because `subscribe` will temrinate the subscription when the sequence terminates (or was supposed to have terminated). This doesn't mean that the problem will always be taken care for us. There is also a method called `unsafeSubscribe`, which won't unsubscribe automatically.
302+
Despite what our observable tried to emit, the end result obeyed the Rx contract. That happened because `subscribe` terminated the subscription when it (very reasonably) thought that the sequence ended. This doesn't mean that the problem will always be taken care for us. There is also a method called `unsafeSubscribe`, which won't unsubscribe automatically.
303303

304304
```java
305305
Observable<Integer> source = Observable.create(o -> {
@@ -337,7 +337,7 @@ Completed
337337
Completed
338338
```
339339

340-
Our subscriber's intended behaviour was identical to the previous example (we created an instance of `Subscriber` because `unsafeSubscribe` doesn't have overloads that take lambdas). However, we can see here we weren't unsubscribed and we kept receiving notifications.
340+
Our subscriber's intended behaviour was identical to the previous example (we created an instance of `Subscriber` because `unsafeSubscribe` doesn't have overloads that take lambdas). However, we can see here we weren't unsubscribed and we kept receiving notifications.
341341

342342
`unsafeSubscribe` is unsafe in other regards as well, such as error handling. It's usefulness is limited. The documentation says that it should only be used for custom operators that use nested subscriptions. To protect such operators from receiving and illegal sequence, we can apply the `serialize` operator
343343

@@ -388,6 +388,16 @@ We here that, despite the fact that we did not unsubscribe, the illegal notifica
388388
If the ability to use your custom operator in the chain like a standard operator is not convincing enough, using `lift` has one more unexpected advantage. Standard operators are also implemented using `lift`, which makes `lift` a hot method at runtime. JVM optimises for `lift` and operators that use `lift` receive a performance boost. That can include your operator, if you use `lift`.
389389

390390

391+
## Choosing between `lift` and `compose`
392+
393+
Both `lift` and `compose` are meta-operators, used for injecting a custom operator into the chain. In both cases, the custom operator can be implemented as a function or a class.
394+
* `compose`: `Observable.Transformer` or `Func<Observable<TSource>, Observable<TReturn>>`
395+
* `lift`: `Observable.Operator` or `Func<Subscriber<TReturn>, Subscriber<TSource>>`
396+
397+
Theoretically, any operator can be implemented as both `Observable.Operator` and `Observable.Transformer`. The choice between the two is a question of convenience, and what kind of boilerplate you want to avoid.
398+
399+
* If the custom operator is a composite of existing operators, `compose` is a natural fit.
400+
* If the custom operator needs to extract values from the pipeline to process them and then push them back, `lift` is a better fit.
391401

392402

393403
#### Continue reading

0 commit comments

Comments
 (0)