Monthly Archives: February 2022

Working with ListenableFuture

References

Baeldung: Guava’s Futures and ListenableFuture

Since we’re working with this type of Future, we want to convert other types into ListenableFuture’s.

From CompletableFuture

import net.javacrumbs.futureconverter.java8guava.FutureConverter

CompletableFuture<String> myCompletableFuture = ...
ListenableFuture<String> myListenableFuture = 
    FutureConverter.toListenableFuture(myCompletableFuture);

To convert a result to a ListenableFuture
Similar to CompletableFuture.completedFuture("Hello World")

ListenableFuture<String> myFutureString =
    Futures.immediateFuture("hello world");

To convert a List of ListenableFuture’s to a ListenableFuture of List
(Similar to CompletableFuture.allOf(myListOfFutures.toArray(new CompletableFuture<?>[0])))

List<ListenableFuture<String>> myListOfFutures = ...
ListenableFuture<List<String>> myFutureOfList =
    Futures.allAsList(myListOfFutures);

To transform results of a ListenableFuture
(Similar to CompletableFuture.thenApply)

public Lion makeFierce(Cat cat) {...}

ListenableFuture<Cat> aCatFuture = ...
ListenableFuture<Lion> = Futures.transform(
    aCatFuture,
    (aCat) -> makeFierce(aCat),
    executor);

When the transform is another async process, then use .transformAsync()
(Similar to CompletableFuture.thenCompose)

public ListenableFuture<Cat> grow(Kitten kitten) {...}

ListenableFuture<Kitten> aKittenFuture = ...
ListenableFuture<Cat> = Futures.transformAsync(
    aKittenFuture,
    (aCat) -> grow(aCat),
    executor);

What if you have futures you had to run one after another? For example, you’re fetching from a service that returns pages of info, and you don’t know how many pages there are.

int max_pages_per_fetch = 10;
int page = 1;
ListenableFuture<List<Page>> bookpages =
    recursivelyFetchAllPages(page);

public ListenableFuture<List<Page>> recursivelyFetchAllPages(int page) {
  ListenableFuture<PageSet> pageSetFuture =
      bookService.fetchPage(page, max_pages_per_fetch);

  return Futures.transformAsync(
      pageSetFuture,
      pageSet -> {
        List<Page> mutablePages = new LinkedList(pageSet.getPages());
        
        if (pageSet.hasMorePages()) {
          ListenableFuture<List<Page>> remainingPagesFuture =
              recursivelyFetchAllPages(page+1);
          return Futures.transform(
              remainingPagesFuture,
              remainingPages -> {
                mutablePages.addAll(remainingPages);
                return mutablePages;
              },
              executor());
      },
      executor());
}

Waiting on several different Futures to complete and combining their result
(Similar to CompletableFuture.allOf)

ListenableFuture<Cat> catFuture = ...
ListenableFuture<Dog> dogFuture = ...
ListenableFuture<PetBasket> basketFuture =
    Futures.whenAllSucceed(catFuture, dogFuture)
        .call(() -> {
          Cat cat = Futures.getDone(catFuture);
          Dog dog = Futures.getDone(dogFuture);
          PetBasket basket = new PetBasket();
          basket.add(cat);
          basket.add(dog);
          return basket;
        }, executor);

Tagged