In Futures.transform, what is the difference between using a Function and an AsyncFunction
Asked Answered
E

4

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?

Euhemerize answered 5/6, 2014 at 0:6 Comment(7)
Do you know what synchronous and asynchronous mean in this context? Explain them to us.Instrumentality
One waits to complete the operation before returning the value, and the other immediately returns a placeholder, that will have the result of the operation at some point of time.Euhemerize
Great. So an example of each is when you need either of those behaviors.Instrumentality
I have seen some code snippets that looks like this: Futures.transform(someFuture, new AsyncFunction<A, B>() { public B apply(A a) {//converts a to B} }); And here the function inside the apply methos is actually not a long running function at all. So why is AsyncFunction required here? Or was this a bad example that I sawEuhemerize
Added the edit. Sorry about that.Euhemerize
As a clarification, what does a.getData return? And what is B? And what is someFuture?Instrumentality
Did you mean to have the method return a ListenableFuture<B>?Instrumentality
G
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.

Gavelkind answered 5/6, 2014 at 15:45 Comment(12)
I'm missing something. How do you tell that the code that transforms A to B is/should be synchronous?Instrumentality
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.Gavelkind
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.)Instrumentality
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.Fortin
@CliveEvans Yeah, but that happens within an AsyncFunction, which happens in a separate thread.Instrumentality
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.Gavelkind
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.Instrumentality
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.Gavelkind
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.Instrumentality
@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.Gavelkind
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.Instrumentality
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.Instrumentality
I
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.

Instrumentality answered 5/6, 2014 at 0:27 Comment(0)
S
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.
Solleret answered 23/6, 2016 at 17:43 Comment(0)
F
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?

Faxun answered 13/7, 2017 at 1:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.