ReactiveX / RxJavaAsyncUtil

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Enhancement Request: Add new function type that can fail

mikebaum opened this issue · comments

Hello,

Perhaps this has already been discussed, if so I apologize in advance for rehashing an existing discussion.

So, now that we have java 8 with lambdas, I was wondering if you would consider adding a new function type, the name could be debated, but lets just call it ThrowingFunc{0, 1, ... , N}. The interface for ThrowingFunc1 would be:

public interface ThrowingFunc1<I, R>
{
    R call( I input ) throws Exception;
}

A series of static methods could be added to the Async class, as follows:

 ....
public static <I, R> Func1<I, Observable<R>> toAsync( ThrowingFunc1<I, R> throwingFunc1 )
 ....

Using this new type and the added helper methods to Async it would be possible to do the following, for example:

public class Example
{
    public String convert( String stringToConvert ) throws ConversionException
    {
        return stringToConvert;
    }

    public static String convertStatic( String stringToConvert ) throws ConversionException
    {
        return stringToConvert;
    }

    public static main( String[] args )
    {
        Example example = new Example();

        Func1<String, Observable<String>> conversionTask1 = Async.toAsync( example::convert );

        Func1<String, Observable<String>> conversionTask2 = Async.toAsync( Example::convertStatic );
    }
}

I left out the implementation of toAsync() for a ThrowingFunc1 since it is trivial, but essentially, if the execution of the function fails with some exception, we would just call onError() with that exception.

The advantages of this interface as I see it are:
1. You could make use of method references and not be forced to wrap a throwing method call in a Func1. It is also reads really nicely since you can see directly in the call to toAsync what method is being called without the noise of a wrapping type.
2. You would not be forced to wrap the thrown exception in a RuntimeException just to tranform the method call into a Func1.

Perhaps there already exists a convenient way to do what I describe if so, I'd be interested to hear how.

Mike

I don't see any problem with that although I never felt the need to use this library. If you whish, you can post a PR with the proposed changes but note that naming the throwing versions toAsync may cause overload resolution problems with javac or in dynamic languages.

Admittedly, yes, Observable.from(Future) and Observable.create(OnSubscribe) cover most cases. However, for front end GUI code (Swing) there exist times when you want to start a background task in a non-lazy fashion. This is usually the case during application startup, where you could be doing lots of IO, to initialize Services and retrieve data. In this case, it is likely that you would start the data retrieval in one place as early as possible and subscribe somewhere else at a later time.

With that in mind, here's my take on the advantages of using the AsyncUtil library for those two factory methods:

  1. Observable.from(Future):
    The issue with this is that the returned Observable blocks on onSubscribe. Blocking on the EDT is definitely a no-no, so a workaround would be to use subscribeOn with another Thread, which seems wasteful, since the Future is already using one Thread to do its work.
  2. Observable.create(OnSubscribe):
    Since this method produces a cold Observable the work will only happen when someone subscribes. Clearly this is a good thing in most cases, but during application startup speed is of the essence, so spinning off a bunch of observables to run in parallel is desirable. Depending on the time between when the observables are created and the consumer subscribes, they may have already finished their work. In this case there would be less waiting.

Okay, great, so I've started on the PR. Just wanted to pass some details/questions by you before I submit my PR.

1. Should we bother creating the ThrowingFunc0 interface since it has the same signature as Callable? If yes, should we make ThrowingFunc0 extend Callable?

2. I propose that we add a start method that takes a Callable, or ThrowingFunc0. Depending on the answer to 1., the following would be the method(s) that should be added:
2a. If ThrowingFunc0 extends Callable:
public static <T, C extends Callable<T>> Observable<T> start(C func)
public static <T, C extends Callable<T>> Observable<T> start(C func, Scheduler scheduler)
2b. If ThrowingFunc0 does not extends Callable:
public static <T> Observable<T> start(ThrowingFunc0<? extends C> func)
public static <T> Observable<T> start(ThrowingFunc0<? extends C> func, Scheduler scheduler)
public static <T> Observable<T> start(Callable<? extends C> func)
public static <T> Observable<T> start(Callable<? extends C> func, Scheduler scheduler)

3. To avoid the issue you brought up regarding the method overload resolution, I propose two options for the names of the methods toAsync and start methods that take a ThrowingFunc:
3a. toAsyncThrowing and startCallable or
3b. throwingToAsync and startCallable

Should we bother creating the ThrowingFunc0 interface since it has the same signature as Callable? If yes, should we make ThrowingFunc0 extend Callable?

Let's stick to Callable.

To avoid the issue you brought up regarding the method overload resolution, I propose two options for the names of the methods toAsync and start methods that take a ThrowingFunc:

startCallable sounds good.