Unlock 2x Faster Async Programming with Java Futures

Last time, we delved into the realm of ListenableFuture. I promised to unveil more sophisticated techniques, specifically transformations and chaining. Let’s embark on a straightforward journey. Suppose we possess a ListenableFuture obtained from some asynchronous service. Additionally,

In our previous article, we explored the world of ListenableFuture. Now, let's dive deeper into more advanced techniques, specifically focusing on transformations and chaining. Imagine we have a ListenableFuture obtained from an asynchronous service. Additionally, we have a simple method:

Document parse(String xml) {//...

Instead of working with the String itself, we need the Document. One approach would be to resolve the Future and process the String. However, a more elegant solution is to apply a transformation once the results are available, making our method behave as if it always returned ListenableFuture. This is remarkably straightforward:

final ListenableFuture future = //... final ListenableFuture documentFuture = Futures.transform(future, new Function<String, Document>() {    @Override    public Document apply(String contents) {        return parse(contents);    }});

Or, for better readability:

final Function<String, Document> parseFun = new Function<String, Document>() {    @Override    public Document apply(String contents) {        return parse(contents);    }}; final ListenableFuture future = //... final ListenableFuture documentFuture = Futures.transform(future, parseFun);

While Java syntax may have its limitations, let's focus on what we've achieved. Futures.transform() doesn't wait for the underlying ListenableFuture to apply the parse() transformation. Instead, it registers a callback, ready to be notified whenever the given future completes. This transformation is applied dynamically and transparently for us at the right moment. We still have a Future, but this time wrapping a Document.

Now, let's take it a step further. We also have an asynchronous, possibly long-running method that calculates the relevance (whatever that means in this context) of a given Document:

ListenableFuture calculateRelevance(Document pageContents) {//...

Can we somehow chain it with the ListenableFuture we already possess? First attempt:

final Function<Document, ListenableFuture> relevanceFun = new Function<Document, ListenableFuture>() {    @Override    public ListenableFuture apply(Document input) {        return calculateRelevance(input);    }}; final ListenableFuture future = //...final ListenableFuture documentFuture = Futures.transform(future, parseFun);final ListenableFuture<ListenableFuture> relevanceFuture = Futures.transform(documentFuture, relevanceFun);

For more information on enhanced asynchronous programming possibilities, visit this link.


Ava Parker

115 Blog posts

Comments