# How to Use Guava's ListenableFuture: A Complete Guide to Async Callbacks and Composition

> Learn how to use Guava's ListenableFuture to handle async callbacks and compose non-blocking pipelines. This complete guide shows you how to get started easily.

- Repository: [Google/guava](https://github.com/google/guava)
- Tags: tutorial
- Published: 2026-08-10

---

**Guava's `ListenableFuture` is a thin extension of `java.util.concurrent.Future` that lets you register callbacks (listeners) to run automatically when an asynchronous computation finishes, enabling non-blocking pipeline construction.**

Guava's concurrency utilities in the `google/guava` repository provide a robust alternative to standard Java futures for building reactive applications. This guide demonstrates how to use Guava's `ListenableFuture` interface and its supporting utilities to create, transform, and combine asynchronous tasks without blocking threads.

## Core Architecture and Source Files

The `ListenableFuture` implementation spans several key files in `com/google/common/util/concurrent/`:

- **[`ListenableFuture.java`](https://github.com/google/guava/blob/main/ListenableFuture.java)** – The interface that defines `addListener(Runnable, Executor)`, allowing listener registration on any future implementation.
- **[`Futures.java`](https://github.com/google/guava/blob/main/Futures.java)** – The central utility class containing static methods for transformation, combination, and callback management.
- **[`SettableFuture.java`](https://github.com/google/guava/blob/main/SettableFuture.java)** – A concrete mutable implementation that lets you manually complete a future with a result or exception.
- **[`TrustedListenableFutureTask.java`](https://github.com/google/guava/blob/main/TrustedListenableFutureTask.java)** – An internal bridge that adapts `Callable` or `AsyncCallable` tasks into `ListenableFuture` instances.
- **[`MoreExecutors.java`](https://github.com/google/guava/blob/main/MoreExecutors.java)** – Provides `listeningDecorator()` to convert standard `ExecutorService` instances into `ListeningExecutorService`.

## Creating ListenableFuture Instances

You cannot instantiate `ListenableFuture` directly; instead, use the factory methods and decorators provided by Guava.

### Wrapping ExecutorService with listeningDecorator

The most common entry point is `MoreExecutors.listeningDecorator()`, which wraps any `ExecutorService` to return `ListenableFuture` from its `submit()` methods.

```java
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;

ListeningExecutorService listeningPool = 
    MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(4));

ListenableFuture<String> future = listeningPool.submit(() -> {
    Thread.sleep(200);
    return "Task complete";
});

```

As implemented in [`MoreExecutors.java`](https://github.com/google/guava/blob/main/MoreExecutors.java), this decorator ensures all submitted tasks return a `ListenableFuture` that supports the listener protocol.

### Using SettableFuture for Manual Completion

When you need to complete a future from non-executor code—such as in tests or callback-based APIs—use `SettableFuture.create()`.

```java
SettableFuture<String> manualFuture = SettableFuture.create();

// Later, from any thread:
manualFuture.set("Success");
// Or: manualFuture.setException(new IOException("Failed"));

```

The [`SettableFuture.java`](https://github.com/google/guava/blob/main/SettableFuture.java) implementation provides a thread-safe, lock-free mechanism for manual completion, making it ideal for test doubles.

### Immediate Futures

For constant values or known exceptions, use `Futures.immediateFuture()` and `Futures.immediateFailedFuture()` to create already-resolved instances without executor overhead.

## Registering Listeners and Callbacks

The primary advantage of `ListenableFuture` over standard `Future` is the ability to react to completion without blocking.

### The addListener Method

The core method defined in [`ListenableFuture.java`](https://github.com/google/guava/blob/main/ListenableFuture.java) is:

```java
void addListener(Runnable listener, Executor executor);

```

If the future is already complete, the listener executes immediately on the calling thread (or the provided executor, depending on the implementation). Otherwise, Guava stores the listener in a lock-free queue and executes it exactly once upon completion.

```java
future.addListener(() -> {
    try {
        System.out.println("Result: " + future.get());
    } catch (Exception e) {
        e.printStackTrace();
    }
}, MoreExecutors.directExecutor());

```

**Note:** `MoreExecutors.directExecutor()` runs the listener on the completing thread—use with caution for long-running tasks.

### Using FutureCallback for Success and Failure Handling

Rather than writing try-catch blocks in every listener, use `Futures.addCallback()` defined in [`Futures.java`](https://github.com/google/guava/blob/main/Futures.java) to register a `FutureCallback` with separate `onSuccess` and `onFailure` methods.

```java
Futures.addCallback(future, new FutureCallback<String>() {
    @Override
    public void onSuccess(String result) {
        System.out.println("Success: " + result);
    }
    
    @Override
    public void onFailure(Throwable t) {
        System.err.println("Failed: " + t.getMessage());
    }
}, listeningPool);

```

## Transforming and Chaining Futures

Guava provides functional composition methods that avoid blocking on intermediate results.

### Synchronous Transformation

`Futures.transform()` applies a function to the result when it becomes available, returning a new `ListenableFuture`.

```java
ListenableFuture<Integer> initial = listeningPool.submit(() -> 42);

ListenableFuture<String> transformed = 
    Futures.transform(initial, 
        i -> "The answer is " + i, 
        MoreExecutors.directExecutor());

```

### Asynchronous Transformation

When the transformation itself returns a future, use `Futures.transformAsync()` (formerly `transform` with an `AsyncFunction`) to flatten the nested structure.

```java
ListenableFuture<Long> asyncTransformed = 
    Futures.transformAsync(transformed,
        s -> listeningPool.submit(() -> s.length() * 1000L),
        listeningPool);

```

These transformation methods, implemented in [`Futures.java`](https://github.com/google/guava/blob/main/Futures.java), propagate cancellation and exceptions automatically through the chain.

## Combining Multiple Futures

Aggregate multiple concurrent operations using combination utilities:

- **`Futures.allAsList()`** – Returns a `ListenableFuture<List<T>>` that succeeds only if all input futures succeed; fails immediately if any input fails.
- **`Futures.whenAllSucceed()`** – Similar to `allAsList` but allows you to specify a combiner function.
- **`Futures.inCompletionOrder()`** – Returns futures in the order they complete, useful for racing queries.

```java
ListenableFuture<String> f1 = listeningPool.submit(() -> "A");
ListenableFuture<String> f2 = listeningPool.submit(() -> "B");

ListenableFuture<List<String>> all = Futures.allAsList(f1, f2);

Futures.addCallback(all, new FutureCallback<List<String>>() {
    @Override
    public void onSuccess(List<String> results) {
        results.forEach(System.out::println);
    }
    
    @Override
    public void onFailure(Throwable t) {
        System.err.println("One task failed: " + t);
    }
}, listeningPool);

```

## Summary

- **ListenableFuture** extends standard Future with `addListener()` for callback-based completion handling.
- Create instances by wrapping executors with `MoreExecutors.listeningDecorator()`, using `SettableFuture.create()` for manual control, or calling utility methods in [`Futures.java`](https://github.com/google/guava/blob/main/Futures.java).
- Register callbacks with `Futures.addCallback()` to handle success and failure paths separately without blocking.
- Chain dependent work using `Futures.transform()` for synchronous mappings and `Futures.transformAsync()` for asynchronous mappings.
- Combine concurrent operations with `Futures.allAsList()` or `Futures.whenAllSucceed()` to aggregate results.

## Frequently Asked Questions

### What is the difference between Future and ListenableFuture?

`java.util.concurrent.Future` only provides blocking access to results via `get()`. **ListenableFuture** adds the `addListener()` method, allowing you to register callbacks that execute automatically upon completion. This enables reactive programming patterns where downstream processing triggers immediately without consuming threads waiting on `get()`.

### When should I use SettableFuture?

Use **SettableFuture** when you need to complete a future manually from arbitrary code rather than from an executor submission. Common scenarios include adapting legacy callback-based APIs, creating test doubles for unit tests, or implementing promise-like patterns where the computation result is supplied by external events.

### How does error handling work with ListenableFuture callbacks?

Exceptions propagate through transformations and callbacks automatically. When using `Futures.addCallback()`, the `onFailure()` method receives the exception. In transformation chains (`transform` or `transformAsync`), if an input future fails, the output future fails immediately with the same exception without executing the transformation function. Always handle exceptions in the `onFailure` callback or catch `ExecutionException` when calling `get()`.

### Is ListenableFuture better than Java 8's CompletableFuture?

**ListenableFuture** predates `CompletableFuture` and offers tighter integration with Guava's ecosystem (caching, service utilities, and Dagger Producers). While `CompletableFuture` provides more flexible composition APIs, `ListenableFuture` remains advantageous when working with legacy Guava-based codebases or when you need the specific aggregation utilities in [`Futures.java`](https://github.com/google/guava/blob/main/Futures.java). Both interfaces solve similar problems but have different method signatures and ecosystem constraints.