0

I have multiple functions that return an Observable<String>. Each function execute command on the file system. I need to execute each function one after another and get the output of the function in the observable. At the end i want a single Observable<String> that contains the output of all the functions in the order of functions calls

Individually, each function work as expected but i need to merge output correctly.

I have try Observable.concatArray(func1, func2, ... ) like this:

    return Observable.concatArray(
        func1(),
        func2(),
        func3(), 
        func4()
    );

but this just preserve the sequence of the observable's events. Not the sequence of the functions. I means if func1 emit events A and A' et func2 emit B and B', i will have A->A'->B->B'. But func2 will start immediately after func1. This cause me problem are func1 need to be finished before func2 can start.

The first function generate directory on the file system via maven. So, a long duration task. The second, writes a file inside this directory. But concatArray launch the second immediately after the first. And the command fail because the directory does not exist at this time.

Is there a way to avoid something ugly like this :

Subject<String> result = PublishSubject.create();
Observable<String> func1Obs = funct1(); 
Observable<String> func2Obs = funct2(); 

func1Obs.subscribe(output -> result.onNext(output));
func1Obs.onDoComplete(() -> {
    func2Obs.subscribe(output -> result.onNext(output);
}
return result;
Scandinave
  • 1,388
  • 1
  • 17
  • 41
  • Please [edit] your question to include a detailed example of your observables and in which order you want to read from them. Also explain in detail why `concat` is not the correct solution in your case. – Progman Apr 11 '20 at 22:27
  • Remember- the whole point of using observables is so that you can *parallelize* tasks. And once you parallelize them, they can now complete *in any order*. If you want/need to *enforce* a certain order, you can a) go back to serial code, or b) use operators like .Then() or When(): http://reactivex.io/documentation/observable.html – FoggyDay Apr 11 '20 at 22:45
  • I will try the second option – Scandinave Apr 11 '20 at 23:06
  • And/Then/When are only available for RxJava 1. I use RxJava 3 – Scandinave Apr 11 '20 at 23:14
  • @Scandinave Please [edit] your question to write the actual returned values from the different observables you have and in which order you wish to read them. Also explain in detail why `concat()`/`concatArray()` is not the solution for your problem. It does indeed consume the first observable first and then read the second one. Also explain what you mean by "sequence of the observable event" and "Not the sequence of the function". – Progman Apr 12 '20 at 06:17
  • I've edit my post. I can't explain more than that – Scandinave Apr 12 '20 at 07:52
  • 1
    @Scandinave Does calling the method `funct2()` already trigger the calculations (creating a file) before even calling `subscribe()` on it? – Progman Apr 12 '20 at 08:15
  • uhm, It seems i have write my observable in the wrong way. I test with the correction you suggest and will tell you if it correct the problem. – Scandinave Apr 12 '20 at 09:54

1 Answers1

1

As Suggest Progman, the error was not with concatArray, this is the method to use. The problem was, that in my list of function, I was using this sort of code :

public Observable<String> func1() {
    Subject<String> result = PublishSubject.create();
    String output = dosomething()
    result.onNext(output);
}

The problem here is that the function doSomething() is call immediately when you create the observable.

The solution is to use either Observable.create() if you need onNext, onComplete, etc... :

public Observable<String> func1() {
    // See how we wrap our instruction inside create method
    return Observable.create( result -> {
        String output = dosomething()
        result.onNext(output);   
    });
}

or Observable.defer(), if you just need to wait for subscribe :

public Observable<String> func1() {
    // See how we wrap our instruction inside create method
    return Observable.defer( () -> dosomething());
}

After that you can call:

return Observable.concatArray(
    func1(),
    func2(),
    func3(), 
    func4()
);
Scandinave
  • 1,388
  • 1
  • 17
  • 41