16

I want to chain async rest service calls and have single callback when they finished.

Is it possible to do it with guava?

Alexey Zakharov
  • 24,694
  • 42
  • 126
  • 197
  • A little bit of an update, you can use [FluentFuture](https://guava.dev/releases/24.0-jre/api/docs/com/google/common/util/concurrent/FluentFuture.html), which is designed exactly for that purpose. It gives you the most importent methods from [Futures](https://guava.dev/releases/24.0-jre/api/docs/com/google/common/util/concurrent/Futures.html) but in a fluent API. You can start the chain using the static factory [from](https://guava.dev/releases/24.0-jre/api/docs/com/google/common/util/concurrent/FluentFuture.html#from-com.google.common.util.concurrent.ListenableFuture-). – Mahmoud Mohsen Apr 06 '20 at 13:59

2 Answers2

23

Futures.chain was removed in version 12.0. The new method of chaining together ListenableFutures is via the Futures.transform method.

https://github.com/google/guava/wiki/ListenableFutureExplained#application

From Guava latest javadoc (16.0.1 as of this writing).

ListenableFuture<RowKey> rowKeyFuture = indexService.lookUp(query);
AsyncFunction<RowKey, QueryResult> queryFunction =
   new AsyncFunction<RowKey, QueryResult>() {
   public ListenableFuture<QueryResult> apply(RowKey rowKey) {
      return dataService.read(rowKey);
   }
};
ListenableFuture<QueryResult> queryFuture = transform(rowKeyFuture, queryFunction);
Iulian Popescu
  • 2,595
  • 4
  • 23
  • 31
Display name
  • 1,109
  • 1
  • 15
  • 31
  • I'm using Futures.transform but I'm getting code blocks nested 12 levels deep. Is there not a way to linearize this? – Sridhar Sarnobat Nov 09 '16 at 22:40
  • 1
    @Sridhar-Sarnobat Sure, just "unwind" it. Create the innermost future, combine it into a new variable, etc. – maaartinus Jul 06 '17 at 15:40
  • @maaartinus hmmmm I'm not sure I get it. An example in the response body would be helpful – Sridhar Sarnobat Jul 07 '17 at 05:41
  • 1
    @Sridhar-Sarnobat It's not my answer and I'm unsure if it was a good edit. I mean something like this: `ListenableFuture f1 = ...; ListenableFuture f2 = transform(f1, fun1, executor); ListenableFuture f3 = transform(f2, fun2, executor); ...`. It's not really different from a function invocation chain like `int x1 = ...; int x2 = fun1(x1); int x3 = fun2(x2); ...`. Instead of computing the values, you compute the futures. You can avoid any nesting by declaring the parts before they're needed. +++ You could do this using "extract local variable", but doing it directly is simpler and clearer. – maaartinus Jul 07 '17 at 10:47
  • Oh I see. It's just extracting local variables. Usually I like to inline single use variables like you said. I was hoping there might be a transform that takes a flat list of futures but I guess not. – Sridhar Sarnobat Jul 07 '17 at 22:59
  • Sridhar-Samobat, if you find yourself in need of a more organized way to manage future events, you might enjoy evaluating how rxJava handles its reactive functions; there are (chainable)methods which order or combine their "Observables" in a variety of ways – anthropic android Nov 20 '18 at 00:40
  • @SridharSarnobat you also might be looking for `Futures.allAsList` or `Futures.successfulAsList` – ianstarz Sep 04 '19 at 20:23
11

You can use Futures.chain for chaining ListenableFutures:

final ListeningExecutorService service1 = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(16));
final ListeningExecutorService service2 = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(16));

ListenableFuture<String> service1result = service1.submit(new Callable<String>() {
    @Override
    public String call() throws Exception {
        return "service1result";
    }
});

ListenableFuture<String> service2result = Futures.chain(service1result, new Function<String, ListenableFuture<String>>() {
    @Override
    public ListenableFuture<String> apply(final @Nullable String input) {
        return service2.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return Joiner.on(" -> ").join(input, "service2result");
            }
        });
    }
});

System.out.format("Result: %s\r\n", service2result.get());

Output of at the code above in the terminal:

> run-main training.Training
[info] Compiling 1 Java source to /home/remeniuk/projects/guava-training/target/scala-2.9.1/classes...
[info] Running training.Training 
Result: service1result -> service2result
Vasil Remeniuk
  • 20,519
  • 6
  • 71
  • 81