Exception handling using launch instead of relying on async
See original GitHub issueI’m writing a small helper class to bridge some asynchronous functionality via coroutines to some of our Java classes. I’m a little confused about handling errors when using launch
coroutines, so I’d like to check my expectations (I’ve read https://github.com/Kotlin/kotlinx.coroutines/issues/61). I’ve gotten this to work successfully using async
instead of launch
, but I really would like to understand how to do this using launch
.
Given the following:
val someData: SomeData? = null
fun returnSomeJob(): Job {
...
//some other operations here
...
return doAsyncThings()
}
fun doAsyncThings(): Job = launch(UI) {
try {
someData = getSomeDataAsync()
} catch (e: Throwable) {
//do something with the error locally, re-throw to percolate
throw e
}
}
suspend fun getSomeDataAsync(): SomeData = getSomeResponse().await().let{ unwrapOrThrow(it) }
fun unwrapOrThrow(someResponse: SomeResponse): SomeData {
when(someResponse.isSuccessful()) {
true -> someResponse.getSomeData()
false -> throw SomeException()
}
}
What would I need to do to make the following helper class forward errors through a callback properly?
class CoroutineHelper {
companion object {
interface CompletionHandler<T> {
fun onSuccess(value: T)
fun onError(error: Throwable)
}
@Suppress("UNCHECKED_CAST")
@JvmStatic
fun <T> handleCompletion(job: Job, handler: CompletionHandler<T>): Job {
return launch(UI) {
when (job) {
//we care about a returned value
is Deferred<*> -> {
try {
"Expecting a return value...".dlog()
(job.await() as T).let { handler.onSuccess(it) }
} catch (e: Throwable) {
handler.onError(e)
}
}
// we do not care about a returned value
else -> {
try {
"Not expecting a return value...".dlog()
job.join()
handler.onSuccess(Unit as T)
} catch (e: Throwable) {
handler.onError(e)
}
}
}
}
}
}
}
I’ve tried adding a CoroutineExceptionHandler
to various contexts up the chain, but no combination of launch(UI + CoroutineExceptionHandler { ... })
on any or all coroutine builders achieves what I’m after. launch(UI + CoroutineExceptionHandler{ launch(UI) { ... } })
allows me to intercept errors in the first launch
, which I can log, but not re-throw in order to percolate. I’d like to bubble them up to catch them in handleCompletion
.
Issue Analytics
- State:
- Created 5 years ago
- Comments:11 (6 by maintainers)
Top GitHub Comments
No sorry. I only meant don’t call callback from
launch
. This would be against the whole philosophy and it would kill the point of using coroutines. But attaching aCoroutineExceptionHandler
is perfectly fine as long as it done is for good reasons (logging for example).Just don’t do it in order to go back to a callback programming style:
Logging is indeed a common case of a good usage of
CoroutineExceptionHandler
. No need to useasync
if your only concern about failure is logging. And no need to callgetCancellationException
either.However, in our code base we decided to attach the log directly to the default
Thread.UncaughtExceptionHandler
, so we don’t have to manually provide aCoroutineExceptionHandler
each time we uselaunch
and we think that all uncaught exception should be logged anyway.What do you mean, CoroutineExceptionHandler also to be avoided?
Logging is a common use case, since if something crashes, you want to be notified even if you don’t need the result…