Developing in Java an asynchronous method with a CompletableFuture
return type we expect the resulting CF to complete normally or exceptionally depending on whether that method succeeds or fails.
Yet, consider for instance that my method writes to an AsynchronousChannel
and got an exception opening that channel. It has not even started writing. So, in this case I am tenting to just let the exception flow to the caller. Is that correct?
Although the caller will have to deal with 2 failure scenarios: 1) exception, or 2) rejected promise.
Or alternatively, should my method catch that exception and return a rejected promise instead?
IMO, option 1) makes the API harder to use because there will be two different paths for communicating errors:
- "Synchronous" exceptions, where the method ends the an exception being thrown.
- "Asynchronous" exceptions, where the method returns a CF, which completes with an exception. Note that it is impossible to avoid this case, because there will always be situations where the errors are only found after the asynchronous path has started (e.g. timeouts).
The programmer now has to ensure both these two paths are correctly handled, instead of just one.
It is also interesting to observe that the behaviour for both C# and Javascript is to always report exceptions thrown inside the body of an async
function via the returned Task
/Promise
, even for exceptions thrown before the first await
, and never by ending the async
function call with an exception.
The same is also true for Kotlin's coroutines, even when using the Unconfined
dispatcher
class SynchronousExceptionExamples {
@Test
fun example() {
log.info("before launch")
val job = GlobalScope.launch(Dispatchers.Unconfined) {
log.info("before throw")
throw Exception("an-error")
}
log.info("after launch")
Thread.sleep(1000)
assertTrue(job.isCancelled)
}
}
will produce
6 [main] INFO SynchronousExceptionExamples - before launch
73 [main @coroutine#1] INFO SynchronousExceptionExamples - before throw
(...)
90 [main] INFO SynchronousExceptionExamples - after launch
Note as the exception occurs in the main
thread, however launch
ends with a proper Job
.