Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the difference between fromCallable and defer?

fromCallable and defer have different implementation. I heard some say that defer helps with recursion but i can't produce any code that shows the difference between them.

i tried with infinite number implementation:

private Observable<Integer> numbers(int start) {
         return Observable.just(start)
                .concatWith(Observable.defer(() -> numbers(start + 1)));
    }

but i still get stack overflow exception when subscribing

like image 539
piotrek Avatar asked Mar 16 '17 16:03

piotrek


2 Answers

fromCallable creates observable that emits single value, then completes.

defer postpones creation of actual observable until it is subscribed to. So you can create multi-value observable based on state at time of subscription. For example:

Observable<Integer> daysOfWeekLeft =
    Observable.defer(() -> 
        Observable.range(currentDayOfWeek(), 7 - currentDayOfWeek()));

Basically you can achieve the same with fromCallable like this:

Observable<Integer> daysOfWeekLeft =
    Observable.fromCallable(() -> currentDayOfWeek())
        .flatMap(day -> Observable.range(day, 7 - day));

PS. Your code for infinite stream of numbers results in infinite recursion. Might need to play with schedulers to avoid that.

like image 79
Yaroslav Stavnichiy Avatar answered Sep 20 '22 11:09

Yaroslav Stavnichiy


I was looking at it terms of Single, which is less complicated than Observable, because it can only have 1 value. (i.e. An observable with 1 emission). The following applies to the other types as well (Observable, Flowable, Completable)

Single.fromCallable

Single.fromCallable actually creates a new Single from the value inside the function it calls. This is why Aaron He said is comment:

fromCallable is kinda a convenient method for Observable.defer(() -> Observable.just(value))

It does that extra step of creating an Observable by using .just. If you call a function fromCallable which already creates a Single, you'll get a nested single.

// Not very useful to get a Single<Single<*>>
val fromCallable: Single<Single<String>> = Single.fromCallable {
    Single.just("Example")
}

// Better in these cases, where the function inside doesn't create a Single already:
 fun heavyFunction(): String { return "Heavy" }
val fromCallable2: Single<String> = Single.fromCallable {heavyFunction()}

Single.defer

Single.defer calls the function inside it, if it returns a Single, then you can use that Single later on. If it doesn't, it won't compile.

val defer: Single<String> = Single.defer {
    Single.just("Example")
}

// This line won't compile:
Single.defer {heavyFunction()}

// You need to wrap it in a Single:
val defer2: Single<String> = Single.defer {Single.just(heavyFunction())}

So, Single.fromCallable creates a Single from a callable, hence the name. This callable that doesn't have to create a Single, it can be anything.

Defer doesn't create a new Single, it just calls its body which should create a Single. That's why it doesn't have the from in its function name.

like image 30
Ben Butterworth Avatar answered Sep 19 '22 11:09

Ben Butterworth