This question is specific to Java and CompletableFuture
.
If I have an async method like the one below,
CompletableFuture<String> get() {
/* Step #1: Do some sync prep work */
String s = doSomethingSync();
/* Step #2: Do something async (returning CompletableFuture) */
return doSomethingAsync(s);
}
If code in step #1 throws, callers of get()
will get an exception before getting the CompletableFuture
it returns, whereas if code inside the CompletableFuture
returned in step #2 throws, callers will only get an exception when they interact with the returned CompletableFuture
.
This suggests that callers of get()
should write some intricate exception handling code to tackle both cases.
Here's an example of another async method, invokeGet()
, that invokes get()
and returns the length of the String
it returns:
CompletableFutre<Integer> InvokeGet() {
try {
CompletableFuture future = get();
return furure.handle((result, throwable) -> {
if (throwable != null) {
/* Handle exception thrown in step #2 in get(), e.g. rethrow */
} else {
return result.length();
}
});
} catch (Exception e) {
/* Handle exception thrown in step #1 in get() */
/* Return some value or throw */
}
}
My question is:
Is get()
poorly written because it requires its callers to do this kind of intricate exception handling, or is this a usual and common pattern? Should async methods returning CompletableFuture
s confine themselves to returning faulted futures in case of errors so their callers don't have to write such error-handling code?