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 CompletableFutures confine themselves to returning faulted futures in case of errors so their callers don't have to write such error-handling code?