0

I have stupid problem with RxJava2.

I need to run two long operations at the same time. I know that I should use Observable.zip() and I use it.

The problem, that my long operations is run one after another and another problem that my long operations starting before I subscribe to them.

Let's imagine that this is my long operation that I should run async.

private String doSomethingLong() {
        Random rand = new Random();
        int value = rand.nextInt(5);
        Timber.i("Do something for [%d] sec [%s]", value, Thread.currentThread().getName());
        try {
            Thread.sleep(value * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
            return String.format(Locale.getDefault(), "Exception [%s]", e.getMessage());
        }
        return String.format(Locale.getDefault(),"Job for [%d] seconds", value);
    }

And let there is a method like test() that will try to make it parallel:

public void test() {

        final long started = System.currentTimeMillis();
        Observable<String> just1 = Observable.just(doSomethingLong()).subscribeOn(Schedulers.newThread());
        Observable<String> just2 = Observable.just(doSomethingLong()).subscribeOn(Schedulers.newThread());


        Observable.zip(just1, just2, new Func2<String, String, Combined>() {
            @Override
            public Combined call(String s, String s2) {
                return new Combined(s, s2);
            }
        }).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<Combined>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(Combined combined) {
                long total = System.currentTimeMillis() - started;
                Timber.i("TOTAL [%d]ms [%s]", total, combined.toString());
            }
        });

    }

When I'm trying to run this I observe that two observables just1 and just2 runs one after another... And it's confused me...

But there is another staff that confused me more... I commented Observable.zip and noticed that just1 and just2 started method doSomethingLong() before I subscribed to them...

Let me show:

public void test() {

        final long started = System.currentTimeMillis();
        Observable<String> just1 = Observable.just(doSomethingLong()).subscribeOn(Schedulers.newThread());
        Observable<String> just2 = Observable.just(doSomethingLong()).subscribeOn(Schedulers.newThread());


//        Observable.zip(just1, just2, new Func2<String, String, Combined>() {
//            @Override
//            public Combined call(String s, String s2) {
//                return new Combined(s, s2);
//            }
//        }).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<Combined>() {
//            @Override
//            public void onCompleted() {
//
//            }
//
//            @Override
//            public void onError(Throwable e) {
//
//            }
//
//            @Override
//            public void onNext(Combined combined) {
//                long total = System.currentTimeMillis() - started;
//                Timber.i("TOTAL [%d]ms [%s]", total, combined.toString());
//            }
//        });

    }

This code make almost same - it's run two times doSomethingLong() one after another...

What I'm expect: 1. I need that doSomethingLong() methods run parallel 2. I'm asking to explain why those methods runs before I start subscribe them. 3. How should I write me code well in this situation. I want that doSomethingLong() methods do not called before I subscribe to them.

Thanks a lot. Hope that I explain problem well.

lazexe
  • 367
  • 4
  • 19
  • 1
    You need to `defer`. The method `doLongStuff` will be called to call `just`. You could also use `create` rather than `just`. – Boris the Spider Jul 10 '19 at 20:09
  • 2
    Possible duplicate of [RxJava Single.just() vs Single.fromCallable()?](https://stackoverflow.com/questions/52670628/rxjava-single-just-vs-single-fromcallable) – Progman Jul 10 '19 at 20:17
  • 1
    This is a common misunderstanding and indeed has been asked many times before. The underlying problem is that parenthesis doesn't magically defer execution of the method - not sure why people think that. You call `doSomethingLong()` to get a value, then create a source with that value at which point that value is a constant reference from RxJava's perspective. – akarnokd Jul 11 '19 at 06:58

2 Answers2

3

Observable.just doesn't run anything when you subscribe. It emits the elements when you subscribe, but your doSomethingLong will run as soon as you pass it as an argument. That's normal and it's how the language works.

What you're looking for is a way to say return this when we subscribe, but also only run it at that time and hopefully on a background thread.

There are a couple of answers for this, here are some:

Using defer

There's an operator called defer which takes a lambda which will be executed once you subscribe:

Observable.defer(() ->  doSomethingLong())

This will only execute doSomethingLong when you subscribe

Using fromCallable

You can create an observable from a lambda. This is known as fromCallable:

Observable.fromCallable(() -> doSomethingLong())

Similarly, this will only run doSomethingLong when you subscribe

Using create

I think this is perhaps the most discouraged way of doing it, since there's a couple of things you have to deal with, but I think for the she of completeness it's ok to mention:

Observable.create( emitter -> {
    if(emitter.isDisposed()) return;

    emitter.onNext(doSomethingLong());
    emitter.onComplete();
});

Again, I'm sure there's more ways of doing this. I just wanted to explain the issue and give some options.

Fred
  • 16,367
  • 6
  • 50
  • 65
0

Create your Observables as Observable.fromCallable{}. And instead of zip use combineLatest()

Docs: http://reactivex.io/RxJava/javadoc/io/reactivex/Observable.html#fromCallable-java.util.concurrent.Callable- http://reactivex.io/documentation/operators/combinelatest.html

LouGi
  • 34
  • 4