9

I know that the apply method of Function returns an object synchronously, and the apply of AsyncFunction runs asynchronously and returns a Future.

Can you give me an example of when to prefer what.

One code snippet that I saw looked something like this:

Futures.transform(someFuture, new AsyncFunction<A, B>() {
  public B apply(A a) {
    if (a != null) {
      return Futures.immediateFuture(a.getData())
    } else {
      return Futures.immediateFailedFuture(checkException(());
    }
  });
});

Since the value inside AsyncFunction is returned as immediate result, why is AsyncFunction needed here? Or is this just a bad example that I came across?

SherinThomas
  • 1,881
  • 4
  • 16
  • 20

4 Answers4

10

The code snippet you found is a bad example, since it uses an AsyncFunction for something that is computed synchronously. It's needlessly verbose.

The code would be cleaner using a standard Function:

Futures.transform(someFuture, new Function<A, B>() {
  public B apply(A a) {
    if (a != null) {
      return a.getData();
    } else {
      throw checkException();
    }
  });
});

You should use an AsyncFunction when the code that transforms A to B is asynchronous. In your example, it's possible that the code was asynchronous at first, and was later changed to use Futures.immediateFuture() / Futures.immediateFailedFuture() by a programmer who didn't bother replacing the AsyncFunction with a Function. Or maybe he just missed the overloaded method.

Etienne Neveu
  • 12,604
  • 9
  • 36
  • 59
  • I'm missing something. How do you tell that the code that transforms A to B is/should be synchronous? – Sotirios Delimanolis Jun 06 '14 at 03:00
  • It just depends on how you transform A to B. If transforming A to B needs some asynchronous I/O or WS call, you'll have to use an AsyncFunction that returns a `Future`. In the example given, the use of `Futures.immediateFuture()` / `Futures.immediateFailedFuture()` denotes a synchronous transformation. Keep in mind that I'm only talking about the synchronicity of the transforming function, since the computation of "someFuture" is probably asynchronous. – Etienne Neveu Jun 06 '14 at 15:25
  • We don't know what `a.getData()` does so I still don't see why you make the assumption that it is synchronous. I think `Futures.immediateFuture` is just used to wrap the result of `a.getData()` because the `apply` method needs to return a `ListenableFuture`. The result of `a.getData()` has to be calculated before that `ListenableFuture` is returned. So, `Futures.immediateFuture` is synchronous relative to `a.getData`, but the `transform` call is asynchronous relative to the `AsyncFunction` and vice versa. (Note that OP's code doesn't seem to compile.) – Sotirios Delimanolis Jun 06 '14 at 15:29
  • 2
    Futures.immediateFuture() is a blocking call. It creates a Future, but in the same thread as it is being called, which is how we know that the example is synchronous. – Clive Evans Jun 06 '14 at 15:47
  • @CliveEvans Yeah, but that happens within an `AsyncFunction`, which happens in a separate thread. – Sotirios Delimanolis Jun 06 '14 at 16:15
  • I might have misused the word "synchronous". I should have said "blocking". What I meant, is that whatever `a.getData()` does, using `Futures.immediateFuture(a.getData())` will block until `getData()` returns. The code inside `getData()` itself might be asynchronous, call a WS on the other side of the planet asynchronously, but if it's not returning a future, it will block. In such a case, using an AsyncFunction is useless. – Etienne Neveu Jun 06 '14 at 16:23
  • Maybe I misunderstand how the `transform` uses the `AsyncFunction`. Does it not submit the `AsyncFuntion` to be done in a separate thread? In this case, what does it matter if `immediateFuture` is blocking? It's still done in a different thread. – Sotirios Delimanolis Jun 06 '14 at 16:28
  • 1
    Having code that blocks on synchronous I/O is bad, even if it is done in a different thread. It's still a JVM thread that's uselessly blocking. Also, unless you specify an Executor, I believe the AsyncFunction is executed in the thread that completes the input Future, which might be problematic. – Etienne Neveu Jun 06 '14 at 16:34
  • `immediateFuture` simply returns an `ImmediateSuccessfulFuture` which has its value set immediately, in this case with the return value of `a.getData()`. What do you mean by blocking here? If this is done in a separate `AsyncFunction` thread (which, sure, might need a custom `Executor` specified), then no threads are blocked until the calling thread invokes `ListenableFuture#get()`. Please response with `@myname` so I can get notified. – Sotirios Delimanolis Jun 06 '14 at 16:49
  • @SotiriosDelimanolis : I meant that it might block on the call to `a.getData()`. I don't think it does in this case, since it's probably a simple getter. But you wrote in a previous comment "We don't know what a.getData() does so I still don't see why you make the assumption that it is synchronous.", so I thought we were discussing that. Yes, `immediateFuture()` returns immediately, but it won't be called until `getData()` completes. So this code blocks on `getData()`. If `getData()` is indeed asynchronous, it should be fixed to return a `Future`, and `immediateFuture()` becomes useless. – Etienne Neveu Jun 06 '14 at 17:01
  • Yeah, but what does that have to do with anything? As I stated earlier, `immediateFuture` is just to wrap the value of `a.getData` in a `ListenableFuture` which is the type expected by the `apply` method. It seems OP's code doesn't compile. – Sotirios Delimanolis Jun 06 '14 at 17:11
  • I don't think the issue here is the `immediateFuture`, it's the fact that the `AsyncFunction` was used without an `Executor`. As you stated earlier, if you don't submit an `Executor` it uses the same thread to apply the function. – Sotirios Delimanolis Jun 06 '14 at 17:32
3

Since the value inside AsyncFunction is returned as immediate result, why is AsyncFunction needed here? Or is this just a bad example that I came across?

Careful. That piece of code is generating an instance of an anonymous class and passing that instance to the transform method. The transform method will use the AsyncFunction is a separate thread. The Future returned chains to retrieve the results from the AsyncFunction and return the result of that Future. This code still involves asynchronous processing.

Use asynchronous processing when you want to and can continue doing work while something else is being executed.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
1

The code snippet you gave is a bad example, whoever wrote it should have used Function.

Futures.transform() is used to follow up on some asynchronous process. Lets call it "ap1" for "asynchronous process 1". When ap1 is done, the function chained by transform will execute. Now, lets discuss the 2 types of functions you can chain with Futures.transform().

    // AsyncFunction() example:
    ListenableFuture<B> future2 = Futures.transform(future1, new AsyncFunction<A, B>() {
      public ListenableFuture<B> apply(A a) {
        if (a != null) {
          ListenableFuture<B> future3 = asyncCallToOtherService(a);
          return future3;
        }
        else {
          return Future.immediateFuture(null);
        }
      });
    });

    // more complex AsyncFunction() example:
    ListenableFuture<B> future2 = Futures.transform(future1, new AsyncFunction<A, B>() {
      public ListenableFuture<B> apply(A a) {
        if (a != null) {
          ListenableFuture<C> future3 = asyncCallToOtherService(a);
          return Futures.transform(future3, new Function<C, B>() {
            @Override
            public B apply(C c) {
              B b = new B();
              b.setResult(c.getStatus());
              return b;
            }
          });
        }
        else {
          return Future.immediateFuture(null);
        }
      });
    });

    // Function() example:
    ListenableFuture<B> future2 = Futures.transform(future1, new Function<A, B>() {
      public B apply(A a) {
        if (a != null) {
          B b = new B();
          b.setResult(a.getStatus());
          return b;
        }
        else {
          return null;
        }
      });
    });
  1. AsyncFunction() should be used when the code inside the apply() spawns another asynchronous process 2 (AP2). This forms a sequence of asynchronous calls that follow each other: AP1->AP2.
  2. Function() should be used when the transformation of the result from AP1 does not require any additional asynchronous processes. Rather all it does is translate the result Object1 into some other Object2 in a synchronous manner.
  3. AsyncFunction() is slightly heavier and results in separate thread, therefore we should use Function() whenever we don't need to spin AP2.
  4. Multiple Functions and AsyncFunctions can be chained together as needed in order to make a complete workflow. The example "more complex AsyncFunction() example" chains A~>C->B transformations, with A~>C being asynchronous.
E M P
  • 11
  • 2
1

Using AsyncFunction makes sense here. If you want to throw some checked exception out of the apply() method in Function, it would complain it's not handled. You can not throw it out of the apply() for it's overridden. So If you want to throw some checked exception, AsyncFunction should be a valid solution. For those saying the given example is bad and given Function example, can you please try compile it?

Tristan.Liu
  • 176
  • 1
  • 7