Is it reasonable to throw an exception from an asynchronous method?

Miguel Gamboa :

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?

Pedro Felix :

IMO, option 1) makes the API harder to use because there will be two different paths for communicating errors:

  1. "Synchronous" exceptions, where the method ends the an exception being thrown.
  2. "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.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=69681&siteId=1