Java – Best practice for parameters in asynchronous APIs in Java

asynchronous-programmingjava

Is there a best practice for using Guava's ListenableFuture in parameters in API calls? Compare these two versions of an interface that internally does a compare-and-set (I'm using Guava's ListenableFuture):

interface MyDao {
    ListenableFuture<Data> getData(String id);
    ListenableFuture<Boolean> updateData(ListenableFuture<Data> data, int newFieldValue);
}

interface MyDao {
    ListenableFuture<Data> getData(String id);
    ListenableFuture<Boolean> updateData(Data data, int newFieldValue);
}

The first looks a bit messy, and the implementation will likely involve a Futures.transform of its own, but you could also argue that it's an asynchronous API, so taking futures as parameters might make sense. Where it gets nice is that it tidies up the business logic when using the interface so it reads like synchronous code:

ListenableFuture<Data> data = dao.getData("123");
ListenableFuture<Boolean> success = dao.updateData(data, 10);

Compare that to

ListenableFuture<Data> data = dao.getData("123");
ListenableFuture<Boolean> success = Futures.transform(data, (Data d) -> dao.updateData(d, 10));

And that's with a lambda. It's hideous in Java 7.

The obvious drawback is the interface looks inconsistent. Why is data a future, but newFieldValue isn't? Should id be a future, too? Along those lines, it makes a trivial call incredibly complicated—almost like Java wrapper objects before autoboxing.

It also doesn't do anything to clean up hideous stack traces when futures fail; they still cascade through every transform.

Best Answer

I think having a ListenableFuture in an API signature method doesn't make it inconsistent.

It's only there to declare explicity to the user an asynchronous computation while hiding the boilerplate thread logic. Like an Optional would declare explicity to the user a possibility of missing data while hiding the boilerplate "if not null" logic.

These 2 links explain a bit more my thoughts: Monadic Java and Use of optional

However, if you think any user of your API potentially want to call updateData but have only an instance of Data you could consider adding the corresponding method to your API.

public interface MyDao {
    ListenableFuture<Boolean> updateData(ListenableFuture<Data> data, int newFieldValue);
    ListenableFuture<Data> getData(String id);
    default ListenableFuture<Boolean> updateData(Data data, int newFieldValue) {
        //TODO: wrap data in a ListenableFuture and call updateData
    }
}
Related Topic